From 6850a17e8ceba35183d5d74a492e35d4a8b56036 Mon Sep 17 00:00:00 2001 From: James Hodgkinson Date: Tue, 17 Oct 2023 18:18:07 +1100 Subject: [PATCH] Windows build fixes and test coverage (#2220) * adding testing for users functions * turning KanidmClient build error into a ClientError * removing a redundant closure --- Cargo.lock | 20 + libs/client/src/lib.rs | 24 +- libs/crypto/src/lib.rs | 9 +- libs/file_permissions/src/windows.rs | 5 +- libs/users/src/lib.rs | 63 +- libs/users/src/unix.rs | 72 +++ project_docs/RELEASE_CHECKLIST.md | 4 +- server/lib/src/be/dbvalue.rs | 1 + server/lib/src/constants/entries.rs | 548 +---------------- server/lib/src/constants/groups.rs | 566 ++++++++++++++++++ server/lib/src/constants/mod.rs | 2 + server/lib/src/entry.rs | 1 + server/lib/src/server/access/mod.rs | 6 +- server/lib/src/server/migrations.rs | 52 +- server/lib/src/server/mod.rs | 6 +- server/testkit/Cargo.toml | 2 + server/testkit/examples/enumerating_access.rs | 119 ++++ tools/cli/Cargo.toml | 4 +- tools/cli/src/opt/ssh_authorizedkeys.rs | 2 +- tools/cli/src/ssh_authorizedkeys.rs | 96 ++- 20 files changed, 914 insertions(+), 688 deletions(-) create mode 100644 libs/users/src/unix.rs create mode 100644 server/lib/src/constants/groups.rs create mode 100644 server/testkit/examples/enumerating_access.rs diff --git a/Cargo.lock b/Cargo.lock index 52a69a461..562497225 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1565,6 +1565,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + [[package]] name = "flate2" version = "1.0.27" @@ -2979,6 +2985,7 @@ dependencies = [ "serde", "serde_json", "shellexpand", + "sketching", "time", "tokio", "tracing", @@ -3173,6 +3180,8 @@ dependencies = [ "lazy_static", "oauth2", "openssl", + "petgraph", + "regex", "reqwest", "serde", "serde_json", @@ -4071,6 +4080,17 @@ dependencies = [ "ucd-trie", ] +[[package]] +name = "petgraph" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" +dependencies = [ + "fixedbitset", + "indexmap 2.0.2", + "serde", +] + [[package]] name = "picky-asn1" version = "0.8.0" diff --git a/libs/client/src/lib.rs b/libs/client/src/lib.rs index 2c6c29bf2..cd1bf98a8 100644 --- a/libs/client/src/lib.rs +++ b/libs/client/src/lib.rs @@ -108,10 +108,10 @@ impl Display for KanidmClientBuilder { #[test] fn test_kanidmclientbuilder_display() { let foo = KanidmClientBuilder::default(); - println!("{}", foo.to_string()); + println!("{}", foo); assert!(foo.to_string().contains("verify_ca")); - let foo = KanidmClientBuilder { + let testclient = KanidmClientBuilder { address: Some("https://example.com".to_string()), verify_ca: true, verify_hostnames: true, @@ -119,13 +119,13 @@ fn test_kanidmclientbuilder_display() { connect_timeout: Some(420), use_system_proxies: true, }; - println!("foo {}", foo.to_string()); - assert!(foo.to_string().contains("verify_ca: true")); - assert!(foo.to_string().contains("verify_hostnames: true")); + println!("foo {}", testclient); + assert!(testclient.to_string().contains("verify_ca: true")); + assert!(testclient.to_string().contains("verify_hostnames: true")); - let badness = foo.danger_accept_invalid_hostnames(true); + let badness = testclient.danger_accept_invalid_hostnames(true); let badness = badness.danger_accept_invalid_certs(true); - println!("badness: {}", badness.to_string()); + println!("badness: {}", badness); assert!(badness.to_string().contains("verify_ca: false")); assert!(badness.to_string().contains("verify_hostnames: false")); } @@ -415,13 +415,15 @@ impl KanidmClientBuilder { */ /// Build the client ready for usage. - pub fn build(self) -> Result { + pub fn build(self) -> Result { // Errghh, how to handle this cleaner. let address = match &self.address { Some(a) => a.clone(), None => { error!("Configuration option 'uri' missing from client configuration, cannot continue client startup without specifying a server to connect to. 🤔"); - std::process::exit(1); + return Err(ClientError::ConfigParseIssue( + "Configuration option 'uri' missing from client configuration, cannot continue client startup without specifying a server to connect to. 🤔".to_string(), + )); } }; @@ -449,7 +451,7 @@ impl KanidmClientBuilder { None => client_builder, }; - let client = client_builder.build()?; + let client = client_builder.build().map_err(ClientError::Transport)?; // Now get the origin. #[allow(clippy::expect_used)] @@ -544,7 +546,7 @@ impl KanidmClient { (*tguard).as_ref().cloned() } - pub fn new_session(&self) -> Result { + pub fn new_session(&self) -> Result { // Copy our builder, and then just process it. let builder = self.builder.clone(); builder.build() diff --git a/libs/crypto/src/lib.rs b/libs/crypto/src/lib.rs index 775c65005..e816298aa 100644 --- a/libs/crypto/src/lib.rs +++ b/libs/crypto/src/lib.rs @@ -84,7 +84,14 @@ impl From for CryptoError { fn from(ossl_err: OpenSSLErrorStack) -> Self { error!(?ossl_err); let code = ossl_err.errors().get(0).map(|e| e.code()).unwrap_or(0); - CryptoError::OpenSSL(code) + #[cfg(not(target_family="windows"))] + let result = CryptoError::OpenSSL(code); + + // this is an .into() because on windows it's a u32 not a u64 + #[cfg(target_family="windows")] + let result = CryptoError::OpenSSL(code.into()); + + result } } diff --git a/libs/file_permissions/src/windows.rs b/libs/file_permissions/src/windows.rs index 316065302..6abf74552 100644 --- a/libs/file_permissions/src/windows.rs +++ b/libs/file_permissions/src/windows.rs @@ -1,6 +1,5 @@ -// #[cfg(target_os = "windows")] -// use std::os::windows::fs::MetadataExt; - +use core::fmt; +use std::{path::Path, fs::Metadata}; /// Check a given file's metadata is read-only for the current user (true = read-only) Stub function if you're building for windows! pub fn readonly(meta: &Metadata) -> bool { eprintln!( diff --git a/libs/users/src/lib.rs b/libs/users/src/lib.rs index 695474ea6..dee8cc522 100644 --- a/libs/users/src/lib.rs +++ b/libs/users/src/lib.rs @@ -1,59 +1,4 @@ -use libc::passwd as c_passwd; -use libc::{gid_t, uid_t}; -use std::ffi::{CStr, OsStr, OsString}; -use std::os::unix::ffi::OsStrExt; -use std::{mem, ptr}; - -pub fn get_current_uid() -> uid_t { - unsafe { libc::getuid() } -} - -pub fn get_effective_uid() -> uid_t { - unsafe { libc::geteuid() } -} - -pub fn get_current_gid() -> gid_t { - unsafe { libc::getgid() } -} - -pub fn get_effective_gid() -> gid_t { - unsafe { libc::getegid() } -} - -pub fn get_user_name_by_uid(uid: uid_t) -> Option { - let mut passwd = unsafe { mem::zeroed::() }; - let mut buf = vec![0; 2048]; - let mut result = ptr::null_mut::(); - - #[cfg(feature = "logging")] - trace!("Running getpwuid_r for user #{}", uid); - - loop { - let r = - unsafe { libc::getpwuid_r(uid, &mut passwd, buf.as_mut_ptr(), buf.len(), &mut result) }; - - if r != libc::ERANGE { - break; - } - - let newsize = buf.len().checked_mul(2)?; - buf.resize(newsize, 0); - } - - if result.is_null() { - // There is no such user, or an error has occurred. - // errno gets set if there’s an error. - return None; - } - - if result != &mut passwd { - // The result of getpwuid_r should be its input passwd. - return None; - } - - let name = unsafe { - OsStr::from_bytes(CStr::from_ptr(result.read().pw_name).to_bytes()).to_os_string() - }; - - Some(name) -} +#[cfg(target_family = "unix")] +pub mod unix; +#[cfg(target_family = "unix")] +pub use unix::*; \ No newline at end of file diff --git a/libs/users/src/unix.rs b/libs/users/src/unix.rs new file mode 100644 index 000000000..f473397a6 --- /dev/null +++ b/libs/users/src/unix.rs @@ -0,0 +1,72 @@ +use libc::passwd as c_passwd; +use libc::{gid_t, uid_t}; +use std::ffi::{CStr, OsStr, OsString}; +use std::os::unix::ffi::OsStrExt; +use std::{mem, ptr}; + +pub fn get_current_uid() -> uid_t { + unsafe { libc::getuid() } +} + +pub fn get_effective_uid() -> uid_t { + unsafe { libc::geteuid() } +} + +pub fn get_current_gid() -> gid_t { + unsafe { libc::getgid() } +} + +pub fn get_effective_gid() -> gid_t { + unsafe { libc::getegid() } +} + +pub fn get_user_name_by_uid(uid: uid_t) -> Option { + let mut passwd = unsafe { mem::zeroed::() }; + let mut buf = vec![0; 2048]; + let mut result = ptr::null_mut::(); + + #[cfg(feature = "logging")] + trace!("Running getpwuid_r for user #{}", uid); + + loop { + let r = + unsafe { libc::getpwuid_r(uid, &mut passwd, buf.as_mut_ptr(), buf.len(), &mut result) }; + + if r != libc::ERANGE { + break; + } + + let newsize = buf.len().checked_mul(2)?; + buf.resize(newsize, 0); + } + + if result.is_null() { + // There is no such user, or an error has occurred. + // errno gets set if there’s an error. + return None; + } + + if result != &mut passwd { + // The result of getpwuid_r should be its input passwd. + return None; + } + + let name = unsafe { + OsStr::from_bytes(CStr::from_ptr(result.read().pw_name).to_bytes()).to_os_string() + }; + + Some(name) +} + + +#[test] +/// just testing these literally don't panic +fn test_get_effective_uid() { + let euid = get_effective_uid(); + assert!(euid > 0); + let egid = get_effective_gid(); + assert!(egid > 0); + + let username = get_user_name_by_uid(get_current_uid()); + assert!(username.is_some()); +} \ No newline at end of file diff --git a/project_docs/RELEASE_CHECKLIST.md b/project_docs/RELEASE_CHECKLIST.md index 9559c6842..8a3672d4d 100644 --- a/project_docs/RELEASE_CHECKLIST.md +++ b/project_docs/RELEASE_CHECKLIST.md @@ -1,3 +1,5 @@ +# Release checklist + ## Pre-Reqs ```bash @@ -65,7 +67,7 @@ cargo install cargo-udeps - [ ] docker buildx use cluster - [ ] `make buildx/kanidmd/x86_64_v3 buildx/kanidmd buildx/kanidm_tools buildx/radiusd` - [ ] `IMAGE_VERSION=latest make buildx/kanidmd/x86_64_v3 buildx/kanidmd buildx/kanidm_tools buildx/radiusd` -- [ ] Update the readme on docker https://hub.docker.com/repository/docker/kanidm/server +- [ ] Update the readme on docker ### Distro diff --git a/server/lib/src/be/dbvalue.rs b/server/lib/src/be/dbvalue.rs index f0cc3f4e1..204c6cd8b 100644 --- a/server/lib/src/be/dbvalue.rs +++ b/server/lib/src/be/dbvalue.rs @@ -405,6 +405,7 @@ pub enum DbValueAccessScopeV1 { } #[derive(Serialize, Deserialize, Debug)] +#[allow(clippy::enum_variant_names)] pub enum DbValueIdentityId { #[serde(rename = "v1i")] V1Internal, diff --git a/server/lib/src/constants/entries.rs b/server/lib/src/constants/entries.rs index 5b1156cd2..295b025f5 100644 --- a/server/lib/src/constants/entries.rs +++ b/server/lib/src/constants/entries.rs @@ -1,5 +1,5 @@ //! Constant Entries for the IDM -use crate::prelude::AttrString; +use crate::prelude::{idm_builtin_admin_groups, AttrString}; use enum_iterator::Sequence; use std::fmt::Display; @@ -9,7 +9,7 @@ use crate::idm::account::Account; use crate::value::PartialValue; use crate::value::Value; use kanidm_proto::constants::*; -use kanidm_proto::v1::{AccountType, Filter, OperationError, UiHint}; +use kanidm_proto::v1::{AccountType, OperationError}; #[cfg(test)] use uuid::uuid; @@ -685,67 +685,6 @@ impl EntryClass { } } -#[derive(Clone, Debug, Default)] -/// Built-in group definitions -pub struct BuiltinGroup { - pub name: &'static str, - description: &'static str, - uuid: uuid::Uuid, - members: Vec, - dyngroup: bool, - dyngroup_filter: Option, - extra_attributes: Vec<(Attribute, Value)>, -} - -impl TryFrom for EntryInitNew { - type Error = OperationError; - - fn try_from(val: BuiltinGroup) -> Result { - let mut entry = EntryInitNew::new(); - - entry.add_ava(Attribute::Name, Value::new_iname(val.name)); - entry.add_ava(Attribute::Description, Value::new_utf8s(val.description)); - // classes for groups - entry.set_ava( - Attribute::Class, - vec![EntryClass::Group.into(), EntryClass::Object.into()], - ); - if val.dyngroup { - if !val.members.is_empty() { - return Err(OperationError::InvalidSchemaState(format!( - "Builtin dyngroup {} has members specified, this is not allowed", - val.name - ))); - } - entry.add_ava(Attribute::Class, EntryClass::DynGroup.to_value()); - match val.dyngroup_filter { - Some(filter) => entry.add_ava(Attribute::DynGroupFilter, Value::JsonFilt(filter)), - None => { - error!( - "No filter specified for dyngroup '{}' this is going to break things!", - val.name - ); - return Err(OperationError::FilterGeneration); - } - }; - } - entry.add_ava(Attribute::Uuid, Value::Uuid(val.uuid)); - entry.set_ava( - Attribute::Member, - val.members - .into_iter() - .map(Value::Refer) - .collect::>(), - ); - // add any extra attributes - val.extra_attributes - .into_iter() - .for_each(|(attr, val)| entry.add_ava(attr, val)); - // all done! - Ok(entry) - } -} - lazy_static! { /// Builtin System Admin account. pub static ref BUILTIN_ACCOUNT_IDM_ADMIN: BuiltinAccount = BuiltinAccount { @@ -756,449 +695,6 @@ lazy_static! { displayname: "IDM Administrator", }; - /// Builtin IDM Administrators Group. - pub static ref BUILTIN_GROUP_IDM_ADMINS_V1: BuiltinGroup = BuiltinGroup { - name: "idm_admins", - description: "Builtin IDM Administrators Group.", - uuid: UUID_IDM_ADMINS, - members: vec![UUID_IDM_ADMIN], - ..Default::default() - }; - - pub static ref BUILTIN_GROUP_SYSTEM_ADMINS_V1: BuiltinGroup = BuiltinGroup { - name: "system_admins", - description: "Builtin System Administrators Group.", - uuid: UUID_SYSTEM_ADMINS, - members: vec![BUILTIN_ACCOUNT_ADMIN.uuid], - ..Default::default() - }; - -// * People read managers - /// Builtin IDM Group for granting elevated people (personal data) read permissions. - pub static ref IDM_PEOPLE_READ_PRIV_V1: BuiltinGroup = BuiltinGroup { - name: "idm_people_read_priv", - description: "Builtin IDM Group for granting elevated people (personal data) read permissions.", - uuid: UUID_IDM_PEOPLE_READ_PRIV, - members: vec![UUID_IDM_PEOPLE_WRITE_PRIV], - ..Default::default() - }; - pub static ref IDM_PEOPLE_WRITE_PRIV_V1: BuiltinGroup = BuiltinGroup { - name: "idm_people_write_priv", - description: "Builtin IDM Group for granting elevated people (personal data) write permissions.", - uuid: UUID_IDM_PEOPLE_WRITE_PRIV, - members: vec![UUID_IDM_PEOPLE_MANAGE_PRIV,UUID_IDM_PEOPLE_EXTEND_PRIV], - ..Default::default() - }; - -// * People write managers - /// Builtin IDM Group for granting elevated people (personal data) write and lifecycle management permissions. - pub static ref IDM_PEOPLE_MANAGE_PRIV_V1: BuiltinGroup = BuiltinGroup { - name: "idm_people_manage_priv", - description: "Builtin IDM Group for granting elevated people (personal data) write and lifecycle management permissions.", - uuid: UUID_IDM_PEOPLE_MANAGE_PRIV, - members: vec![UUID_IDM_ADMINS], - ..Default::default() - }; - - /// Builtin IDM Group for importing passwords to person accounts - intended for service account membership only. - pub static ref IDM_PEOPLE_ACCOUNT_PASSWORD_IMPORT_PRIV_V1: BuiltinGroup = BuiltinGroup { - name: "idm_people_account_password_import_priv", - description: "Builtin IDM Group for importing passwords to person accounts - intended for service account membership only.", - uuid: UUID_IDM_PEOPLE_ACCOUNT_PASSWORD_IMPORT_PRIV, - members: vec![UUID_IDM_ADMINS], - ..Default::default() - }; - - /// Builtin IDM Group for allowing the ability to extend accounts to have the "person" flag set. - pub static ref IDM_PEOPLE_EXTEND_PRIV_V1: BuiltinGroup = BuiltinGroup { - name: "idm_people_extend_priv", - description: "Builtin System Administrators Group.", - uuid: UUID_IDM_PEOPLE_EXTEND_PRIV, - members: vec![UUID_SYSTEM_ADMINS], - ..Default::default() - }; - /// Self-write of mail - pub static ref IDM_PEOPLE_SELF_WRITE_MAIL_PRIV_V1: BuiltinGroup = BuiltinGroup { - name: "idm_people_self_write_mail_priv", - description: "Builtin IDM Group for people accounts to update their own mail.", - uuid: UUID_IDM_PEOPLE_SELF_WRITE_MAIL_PRIV, - members: Vec::new(), - ..Default::default() - }; - - /// Builtin IDM Group for granting elevated high privilege people (personal data) read permissions. - pub static ref IDM_HP_PEOPLE_READ_PRIV_V1: BuiltinGroup = BuiltinGroup { - name: "idm_hp_people_read_priv", - description: "Builtin IDM Group for granting elevated high privilege people (personal data) read permissions.", - uuid: UUID_IDM_HP_PEOPLE_READ_PRIV, - members: vec![UUID_IDM_HP_PEOPLE_WRITE_PRIV], - ..Default::default() - }; - - /// Builtin IDM Group for granting elevated high privilege people (personal data) write permissions. - pub static ref IDM_HP_PEOPLE_WRITE_PRIV_V1: BuiltinGroup = BuiltinGroup { - name: "idm_hp_people_write_priv", - description: "Builtin IDM Group for granting elevated high privilege people (personal data) write permissions.", - uuid: UUID_IDM_HP_PEOPLE_WRITE_PRIV, - members: vec![UUID_IDM_HP_PEOPLE_EXTEND_PRIV], - ..Default::default() - }; - - /// Builtin IDM Group for extending high privilege accounts to be people. - pub static ref IDM_HP_PEOPLE_EXTEND_PRIV_V1: BuiltinGroup = BuiltinGroup { - name: "idm_hp_people_extend_priv", - description: "Builtin IDM Group for extending high privilege accounts to be people.", - uuid: UUID_IDM_HP_PEOPLE_EXTEND_PRIV, - members: vec![UUID_SYSTEM_ADMINS], - ..Default::default() - }; - -// * group write manager (no read, everyone has read via the anon, etc) - - /// Builtin IDM Group for granting elevated group write and lifecycle permissions. - pub static ref IDM_GROUP_MANAGE_PRIV_V1: BuiltinGroup = BuiltinGroup { - name: "idm_group_manage_priv", - description: "Builtin IDM Group for granting elevated group write and lifecycle permissions.", - uuid: UUID_IDM_GROUP_MANAGE_PRIV, - members: vec![ - BUILTIN_GROUP_IDM_ADMINS_V1.uuid, - BUILTIN_GROUP_SYSTEM_ADMINS_V1.uuid, - ], - ..Default::default() - }; - - /// Builtin IDM Group for granting elevated group write and lifecycle permissions. - pub static ref IDM_GROUP_WRITE_PRIV_V1: BuiltinGroup = BuiltinGroup { - name: "idm_group_write_priv", - description: "Builtin IDM Group for granting elevated group write permissions.", - uuid: UUID_IDM_GROUP_WRITE_PRIV, - members: vec![ - UUID_IDM_GROUP_MANAGE_PRIV - ], - ..Default::default() - }; - - /// Builtin IDM Group for granting unix group extension permissions. - pub static ref IDM_GROUP_UNIX_EXTEND_PRIV_V1: BuiltinGroup = BuiltinGroup { - name: "idm_group_unix_extend_priv", - description: "Builtin IDM Group for granting UNIX group extension permissions.", - uuid: UUID_IDM_GROUP_UNIX_EXTEND_PRIV, - members: vec![ - UUID_IDM_ADMINS - ], - ..Default::default() - }; - - /// Account read manager - pub static ref IDM_ACCOUNT_READ_PRIV_V1: BuiltinGroup = BuiltinGroup { - name: "idm_account_read_priv", - description: "Builtin IDM Group for granting elevated account read permissions.", - uuid: UUID_IDM_ACCOUNT_READ_PRIV, - members: vec![ - UUID_IDM_ACCOUNT_WRITE_PRIV, - ], - ..Default::default() - }; - - pub static ref IDM_ACCOUNT_MANAGE_PRIV_V1: BuiltinGroup = BuiltinGroup { - name: "idm_account_manage_priv", - description: "Builtin IDM Group for granting elevated account write and lifecycle permissions.", - uuid: UUID_IDM_ACCOUNT_MANAGE_PRIV, - members: vec![ - UUID_IDM_ADMINS, - ], - ..Default::default() - }; - - pub static ref IDM_ACCOUNT_WRITE_PRIV_V1: BuiltinGroup = BuiltinGroup { - name: "idm_account_write_priv", - description: "Builtin IDM Group for granting elevated account write permissions.", - uuid: UUID_IDM_ACCOUNT_WRITE_PRIV, - members: vec![ - UUID_IDM_ACCOUNT_MANAGE_PRIV, - ], - ..Default::default() - }; - - pub static ref IDM_ACCOUNT_UNIX_EXTEND_PRIV_V1: BuiltinGroup = BuiltinGroup { - name: "idm_account_unix_extend_priv", - description: "Builtin IDM Group for granting account unix extend permissions.", - uuid: UUID_IDM_ACCOUNT_UNIX_EXTEND_PRIV, - members: vec![ - UUID_IDM_ADMINS, - ], - ..Default::default() - }; - - /// Builtin IDM Group for RADIUS secret write for all non-hp accounts. - pub static ref IDM_RADIUS_SECRET_WRITE_PRIV_V1: BuiltinGroup = BuiltinGroup { - name: "idm_radius_secret_write_priv", - description: "Builtin IDM Group for RADIUS secret write for all non-hp accounts.", - uuid: UUID_IDM_RADIUS_SECRET_WRITE_PRIV_V1, - members: vec![ - UUID_IDM_ADMINS, - ], - ..Default::default() - }; - - /// Builtin IDM Group for RADIUS secret reading for all non-hp accounts. - pub static ref IDM_RADIUS_SECRET_READ_PRIV_V1: BuiltinGroup = BuiltinGroup { - name: "idm_radius_secret_read_priv", - description: "Builtin IDM Group for RADIUS secret reading for all non-hp accounts.", - uuid: UUID_IDM_RADIUS_SECRET_READ_PRIV_V1, - members: vec![ - UUID_IDM_RADIUS_SECRET_WRITE_PRIV_V1, - ], - ..Default::default() - }; - - /// Builtin IDM Group for RADIUS server access delegation. - pub static ref IDM_RADIUS_SERVERS_V1: BuiltinGroup = BuiltinGroup { - name: "idm_radius_servers", - description: "Builtin IDM Group for RADIUS server access delegation.", - uuid: UUID_IDM_RADIUS_SERVERS, - members: vec![ - ], - ..Default::default() - }; - - /// High privilege account read manager - pub static ref IDM_HP_ACCOUNT_READ_PRIV_V1: BuiltinGroup = BuiltinGroup { - name: "idm_hp_account_read_priv", - description: "Builtin IDM Group for granting elevated account read permissions over high privilege accounts.", - uuid: UUID_IDM_HP_ACCOUNT_READ_PRIV, - members: vec![ - UUID_IDM_HP_ACCOUNT_WRITE_PRIV - ], - ..Default::default() - }; - - /// Builtin IDM Group for granting elevated account write permissions over high privilege accounts. - pub static ref IDM_HP_ACCOUNT_MANAGE_PRIV_V1: BuiltinGroup = BuiltinGroup { - name: "idm_hp_account_manage_priv", - description: "Builtin IDM Group for granting elevated account write and lifecycle permissions over high privilege accounts.", - uuid: UUID_IDM_HP_ACCOUNT_MANAGE_PRIV, - members: vec![ - UUID_SYSTEM_ADMINS, - ], - ..Default::default() - }; - /// Builtin IDM Group for granting elevated account write permissions over high privilege accounts. - pub static ref IDM_HP_ACCOUNT_WRITE_PRIV_V1: BuiltinGroup = BuiltinGroup { - name: "idm_hp_account_write_priv", - description: "Builtin IDM Group for granting elevated account write permissions over high privilege accounts.", - uuid: UUID_IDM_HP_ACCOUNT_WRITE_PRIV, - members: vec![ - UUID_IDM_HP_ACCOUNT_MANAGE_PRIV, - ], - ..Default::default() - }; - - /// Builtin IDM Group for granting account unix extend permissions for high privilege accounts. - pub static ref IDM_HP_ACCOUNT_UNIX_EXTEND_PRIV_V1: BuiltinGroup = BuiltinGroup { - name: "idm_hp_account_unix_extend_priv", - description: "Builtin IDM Group for granting account UNIX extend permissions for high privilege accounts.", - uuid: UUID_IDM_HP_ACCOUNT_UNIX_EXTEND_PRIV, - members: vec![ - UUID_SYSTEM_ADMINS, - ], - ..Default::default() - }; - - /// * Schema write manager - pub static ref IDM_SCHEMA_MANAGE_PRIV_V1: BuiltinGroup = BuiltinGroup { - name: "idm_schema_manage_priv", - description: "Builtin IDM Group for granting elevated schema write and management permissions.", - uuid: UUID_IDM_SCHEMA_MANAGE_PRIV, - members: vec![ - UUID_SYSTEM_ADMINS, - ], - ..Default::default() - }; - - /// ACP read/write manager - pub static ref IDM_ACP_MANAGE_PRIV_V1: BuiltinGroup = BuiltinGroup { - name: "idm_acp_manage_priv", - description: "Builtin IDM Group for granting control over all access control profile modifications.", - uuid: UUID_IDM_ACP_MANAGE_PRIV, - members: vec![ - UUID_SYSTEM_ADMINS, - ], - ..Default::default() - }; - - /// Builtin IDM Group for granting elevated group write and lifecycle privileges for high privilege groups. - pub static ref IDM_HP_GROUP_MANAGE_PRIV_V1: BuiltinGroup = BuiltinGroup { - name: "idm_hp_group_manage_priv", - description: "Builtin IDM Group for granting elevated group write and lifecycle privileges for high privilege groups.", - uuid: UUID_IDM_HP_GROUP_MANAGE_PRIV, - members: vec![ - UUID_SYSTEM_ADMINS, - ], - ..Default::default() - }; - - /// Builtin IDM Group for granting elevated group write privileges for high privilege groups. - pub static ref IDM_HP_GROUP_WRITE_PRIV_V1: BuiltinGroup = BuiltinGroup { - name: "idm_hp_group_write_priv", - description: "Builtin IDM Group for granting elevated group write privileges for high privilege groups.", - uuid: UUID_IDM_HP_GROUP_WRITE_PRIV, - members: vec![ - UUID_IDM_HP_GROUP_MANAGE_PRIV, - ], - ..Default::default() - }; - -} -// at some point vs code just gives up on syntax highlighting inside lazy_static... -lazy_static! { - - /// Builtin IDM Group for granting unix group extension permissions for high privilege groups. - pub static ref IDM_HP_GROUP_UNIX_EXTEND_PRIV_V1: BuiltinGroup = BuiltinGroup { - name: "idm_hp_group_unix_extend_priv", - description: "Builtin IDM Group for granting unix group extension permissions for high privilege groups.", - uuid: UUID_IDM_HP_GROUP_UNIX_EXTEND_PRIV, - members: vec![ - UUID_SYSTEM_ADMINS, - ], - ..Default::default() - }; - - /// Builtin IDM Group for granting local domain administration rights and trust administration rights - pub static ref DOMAIN_ADMINS: BuiltinGroup = BuiltinGroup { - name: "domain_admins", - description: "Builtin IDM Group for granting local domain administration rights and trust administration rights.", - uuid: UUID_DOMAIN_ADMINS, - members: vec![ - UUID_ADMIN, - ], - ..Default::default() - }; - - - /// Builtin IDM Group for managing oauth2 resource server integrations to this authentication domain. - pub static ref IDM_HP_OAUTH2_MANAGE_PRIV_V1: BuiltinGroup = BuiltinGroup { - name: "idm_hp_oauth2_manage_priv", - description: "Builtin IDM Group for managing oauth2 resource server integrations to this authentication domain.", - uuid: UUID_IDM_HP_OAUTH2_MANAGE_PRIV, - members: vec![ - UUID_SYSTEM_ADMINS, - ], - ..Default::default() - }; - - /// Builtin IDM Group for allowing migrations of service accounts into persons - pub static ref IDM_HP_SERVICE_ACCOUNT_INTO_PERSON_MIGRATE_PRIV: BuiltinGroup = BuiltinGroup { - name: "idm_hp_service_account_into_person_migrate_priv", - description:"Builtin IDM Group for allowing migrations of service accounts into persons", - uuid: UUID_IDM_HP_SERVICE_ACCOUNT_INTO_PERSON_MIGRATE_PRIV, - members: vec![ - UUID_SYSTEM_ADMINS, - ], - ..Default::default() - }; - - - /// Builtin IDM Group for allowing migrations of service accounts into persons - pub static ref IDM_HP_SYNC_ACCOUNT_MANAGE_PRIV: BuiltinGroup = BuiltinGroup { - name: "idm_hp_sync_account_manage_priv", - description: "Builtin IDM Group for managing synchronisation from external identity sources", - uuid: UUID_IDM_HP_SYNC_ACCOUNT_MANAGE_PRIV, - members: vec![ - UUID_SYSTEM_ADMINS, - ], - ..Default::default() - }; - - /// Builtin IDM Group for extending high privilege accounts to be people. - pub static ref IDM_ALL_PERSONS: BuiltinGroup = BuiltinGroup { - name: "idm_all_persons", - description: "Builtin IDM Group for extending high privilege accounts to be people.", - uuid: UUID_IDM_ALL_PERSONS, - members: Vec::new(), - dyngroup: true, - dyngroup_filter: Some( - Filter::And(vec![ - Filter::Eq(Attribute::Class.to_string(), EntryClass::Person.to_string()), - Filter::Eq(Attribute::Class.to_string(), EntryClass::Account.to_string()), - ]) - ), - ..Default::default() - }; - - /// Builtin IDM Group for extending high privilege accounts to be people. - pub static ref IDM_ALL_ACCOUNTS: BuiltinGroup = BuiltinGroup { - name: "idm_all_accounts", - description: "Builtin IDM dynamic group containing all entries that can authenticate.", - uuid: UUID_IDM_ALL_ACCOUNTS, - members: Vec::new(), - dyngroup: true, - dyngroup_filter: Some( - Filter::Eq(Attribute::Class.to_string(), EntryClass::Account.to_string()), - ), - ..Default::default() - }; - - - pub static ref IDM_UI_ENABLE_EXPERIMENTAL_FEATURES: BuiltinGroup = BuiltinGroup { - name: "idm_ui_enable_experimental_features", - description: "Members of this group will have access to experimental web UI features.", - uuid: UUID_IDM_UI_ENABLE_EXPERIMENTAL_FEATURES, - extra_attributes: vec![ - (Attribute::GrantUiHint, Value::UiHint(UiHint::ExperimentalFeatures)) - ], - ..Default::default() - }; - - /// Members of this group will have access to read the mail attribute of all persons and service accounts. - pub static ref IDM_ACCOUNT_MAIL_READ_PRIV: BuiltinGroup = BuiltinGroup { - name: "idm_account_mail_read_priv", - description: "Members of this group will have access to read the mail attribute of all persons and service accounts.", - uuid: UUID_IDM_ACCOUNT_MAIL_READ_PRIV, - ..Default::default() - }; - - /// This must be the last group to init to include the UUID of the other high priv groups. - pub static ref IDM_HIGH_PRIVILEGE_V1: BuiltinGroup = BuiltinGroup { - name: "idm_high_privilege", - uuid: UUID_IDM_HIGH_PRIVILEGE, - description: "Builtin IDM provided groups with high levels of access that should be audited and limited in modification.", - members: vec![ - UUID_IDM_ADMINS, - UUID_IDM_PEOPLE_READ_PRIV, - UUID_IDM_PEOPLE_WRITE_PRIV, - UUID_IDM_GROUP_WRITE_PRIV, - UUID_IDM_ACCOUNT_READ_PRIV, - UUID_IDM_ACCOUNT_WRITE_PRIV, - UUID_IDM_RADIUS_SERVERS, - UUID_IDM_HP_ACCOUNT_READ_PRIV, - UUID_IDM_HP_ACCOUNT_WRITE_PRIV, - UUID_IDM_SCHEMA_MANAGE_PRIV, - UUID_IDM_ACP_MANAGE_PRIV, - UUID_IDM_HP_GROUP_WRITE_PRIV, - UUID_IDM_PEOPLE_MANAGE_PRIV, - UUID_IDM_ACCOUNT_MANAGE_PRIV, - UUID_IDM_GROUP_MANAGE_PRIV, - UUID_IDM_HP_ACCOUNT_MANAGE_PRIV, - UUID_IDM_HP_GROUP_MANAGE_PRIV, - UUID_SYSTEM_ADMINS, - UUID_DOMAIN_ADMINS, - UUID_IDM_PEOPLE_ACCOUNT_PASSWORD_IMPORT_PRIV, - UUID_IDM_PEOPLE_EXTEND_PRIV, - UUID_IDM_HP_ACCOUNT_UNIX_EXTEND_PRIV, - UUID_IDM_HP_GROUP_UNIX_EXTEND_PRIV, - UUID_IDM_HP_OAUTH2_MANAGE_PRIV, - UUID_IDM_RADIUS_SECRET_WRITE_PRIV_V1, - UUID_IDM_RADIUS_SECRET_READ_PRIV_V1, - UUID_IDM_HP_SERVICE_ACCOUNT_INTO_PERSON_MIGRATE_PRIV, - UUID_IDM_HP_SYNC_ACCOUNT_MANAGE_PRIV, - UUID_IDM_HIGH_PRIVILEGE, - ], - dyngroup: false, - dyngroup_filter: None, - extra_attributes: Vec::new(), - }; - pub static ref E_SYSTEM_INFO_V1: EntryInitNew = entry_init!( (Attribute::Class, EntryClass::Object.to_value()), (Attribute::Class, EntryClass::SystemInfo.to_value()), @@ -1305,31 +801,21 @@ lazy_static! { }; } +pub fn builtin_accounts() -> Vec<&'static BuiltinAccount> { + vec![ + &BUILTIN_ACCOUNT_ANONYMOUS_V1, + &BUILTIN_ACCOUNT_ADMIN, + &BUILTIN_ACCOUNT_IDM_ADMIN, + ] +} + // ============ TEST DATA ============ #[cfg(test)] pub const UUID_TESTPERSON_1: Uuid = uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"); -#[cfg(test)] -pub const JSON_TESTPERSON1: &str = r#"{ - "attrs": { - "class": ["object"], - "name": ["testperson1"], - "uuid": ["cc8e95b4-c24f-4d68-ba54-8bed76f63930"] - } -}"#; - #[cfg(test)] pub const UUID_TESTPERSON_2: Uuid = uuid!("538faac7-4d29-473b-a59d-23023ac19955"); -#[cfg(test)] -pub const JSON_TESTPERSON2: &str = r#"{ - "attrs": { - "class": ["object"], - "name": ["testperson2"], - "uuid": ["538faac7-4d29-473b-a59d-23023ac19955"] - } -}"#; - #[cfg(test)] lazy_static! { pub static ref E_TESTPERSON_1: EntryInitNew = entry_init!( @@ -1343,3 +829,17 @@ lazy_static! { (Attribute::Uuid, Value::Uuid(UUID_TESTPERSON_2)) ); } + +/// Build a list of internal admin entries +pub fn idm_builtin_admin_entries() -> Result, OperationError> { + let mut res: Vec = vec![ + BUILTIN_ACCOUNT_ANONYMOUS_V1.clone().into(), + BUILTIN_ACCOUNT_ADMIN.clone().into(), + BUILTIN_ACCOUNT_IDM_ADMIN.clone().into(), + ]; + for group in idm_builtin_admin_groups() { + let g: EntryInitNew = group.clone().try_into()?; + res.push(g); + } + Ok(res) +} diff --git a/server/lib/src/constants/groups.rs b/server/lib/src/constants/groups.rs new file mode 100644 index 000000000..3360aeffa --- /dev/null +++ b/server/lib/src/constants/groups.rs @@ -0,0 +1,566 @@ +use crate::entry::EntryInitNew; +use crate::prelude::*; + +use kanidm_proto::v1::{Filter, OperationError, UiHint}; + +#[derive(Clone, Debug, Default)] +/// Built-in group definitions +pub struct BuiltinGroup { + pub name: &'static str, + pub description: &'static str, + pub uuid: uuid::Uuid, + pub members: Vec, + pub dyngroup: bool, + pub dyngroup_filter: Option, + pub extra_attributes: Vec<(Attribute, Value)>, +} + +impl TryFrom for EntryInitNew { + type Error = OperationError; + + fn try_from(val: BuiltinGroup) -> Result { + let mut entry = EntryInitNew::new(); + + entry.add_ava(Attribute::Name, Value::new_iname(val.name)); + entry.add_ava(Attribute::Description, Value::new_utf8s(val.description)); + // classes for groups + entry.set_ava( + Attribute::Class, + vec![EntryClass::Group.into(), EntryClass::Object.into()], + ); + if val.dyngroup { + if !val.members.is_empty() { + return Err(OperationError::InvalidSchemaState(format!( + "Builtin dyngroup {} has members specified, this is not allowed", + val.name + ))); + } + entry.add_ava(Attribute::Class, EntryClass::DynGroup.to_value()); + match val.dyngroup_filter { + Some(filter) => entry.add_ava(Attribute::DynGroupFilter, Value::JsonFilt(filter)), + None => { + error!( + "No filter specified for dyngroup '{}' this is going to break things!", + val.name + ); + return Err(OperationError::FilterGeneration); + } + }; + } + entry.add_ava(Attribute::Uuid, Value::Uuid(val.uuid)); + entry.set_ava( + Attribute::Member, + val.members + .into_iter() + .map(Value::Refer) + .collect::>(), + ); + // add any extra attributes + val.extra_attributes + .into_iter() + .for_each(|(attr, val)| entry.add_ava(attr, val)); + // all done! + Ok(entry) + } +} + +lazy_static! { + + + /// Builtin IDM Administrators Group. + pub static ref BUILTIN_GROUP_IDM_ADMINS_V1: BuiltinGroup = BuiltinGroup { + name: "idm_admins", + description: "Builtin IDM Administrators Group.", + uuid: UUID_IDM_ADMINS, + members: vec![UUID_IDM_ADMIN], + ..Default::default() + }; + + pub static ref BUILTIN_GROUP_SYSTEM_ADMINS_V1: BuiltinGroup = BuiltinGroup { + name: "system_admins", + description: "Builtin System Administrators Group.", + uuid: UUID_SYSTEM_ADMINS, + members: vec![BUILTIN_ACCOUNT_ADMIN.uuid], + ..Default::default() + }; + +// * People read managers + /// Builtin IDM Group for granting elevated people (personal data) read permissions. + pub static ref IDM_PEOPLE_READ_PRIV_V1: BuiltinGroup = BuiltinGroup { + name: "idm_people_read_priv", + description: "Builtin IDM Group for granting elevated people (personal data) read permissions.", + uuid: UUID_IDM_PEOPLE_READ_PRIV, + members: vec![UUID_IDM_PEOPLE_WRITE_PRIV], + ..Default::default() + }; + pub static ref IDM_PEOPLE_WRITE_PRIV_V1: BuiltinGroup = BuiltinGroup { + name: "idm_people_write_priv", + description: "Builtin IDM Group for granting elevated people (personal data) write permissions.", + uuid: UUID_IDM_PEOPLE_WRITE_PRIV, + members: vec![UUID_IDM_PEOPLE_MANAGE_PRIV,UUID_IDM_PEOPLE_EXTEND_PRIV], + ..Default::default() + }; + +// * People write managers + /// Builtin IDM Group for granting elevated people (personal data) write and lifecycle management permissions. + pub static ref IDM_PEOPLE_MANAGE_PRIV_V1: BuiltinGroup = BuiltinGroup { + name: "idm_people_manage_priv", + description: "Builtin IDM Group for granting elevated people (personal data) write and lifecycle management permissions.", + uuid: UUID_IDM_PEOPLE_MANAGE_PRIV, + members: vec![UUID_IDM_ADMINS], + ..Default::default() + }; + + /// Builtin IDM Group for importing passwords to person accounts - intended for service account membership only. + pub static ref IDM_PEOPLE_ACCOUNT_PASSWORD_IMPORT_PRIV_V1: BuiltinGroup = BuiltinGroup { + name: "idm_people_account_password_import_priv", + description: "Builtin IDM Group for importing passwords to person accounts - intended for service account membership only.", + uuid: UUID_IDM_PEOPLE_ACCOUNT_PASSWORD_IMPORT_PRIV, + members: vec![UUID_IDM_ADMINS], + ..Default::default() + }; + + /// Builtin IDM Group for allowing the ability to extend accounts to have the "person" flag set. + pub static ref IDM_PEOPLE_EXTEND_PRIV_V1: BuiltinGroup = BuiltinGroup { + name: "idm_people_extend_priv", + description: "Builtin System Administrators Group.", + uuid: UUID_IDM_PEOPLE_EXTEND_PRIV, + members: vec![UUID_SYSTEM_ADMINS], + ..Default::default() + }; + /// Self-write of mail + pub static ref IDM_PEOPLE_SELF_WRITE_MAIL_PRIV_V1: BuiltinGroup = BuiltinGroup { + name: "idm_people_self_write_mail_priv", + description: "Builtin IDM Group for people accounts to update their own mail.", + uuid: UUID_IDM_PEOPLE_SELF_WRITE_MAIL_PRIV, + members: Vec::new(), + ..Default::default() + }; + + /// Builtin IDM Group for granting elevated high privilege people (personal data) read permissions. + pub static ref IDM_HP_PEOPLE_READ_PRIV_V1: BuiltinGroup = BuiltinGroup { + name: "idm_hp_people_read_priv", + description: "Builtin IDM Group for granting elevated high privilege people (personal data) read permissions.", + uuid: UUID_IDM_HP_PEOPLE_READ_PRIV, + members: vec![UUID_IDM_HP_PEOPLE_WRITE_PRIV], + ..Default::default() + }; + + /// Builtin IDM Group for granting elevated high privilege people (personal data) write permissions. + pub static ref IDM_HP_PEOPLE_WRITE_PRIV_V1: BuiltinGroup = BuiltinGroup { + name: "idm_hp_people_write_priv", + description: "Builtin IDM Group for granting elevated high privilege people (personal data) write permissions.", + uuid: UUID_IDM_HP_PEOPLE_WRITE_PRIV, + members: vec![UUID_IDM_HP_PEOPLE_EXTEND_PRIV], + ..Default::default() + }; + + /// Builtin IDM Group for extending high privilege accounts to be people. + pub static ref IDM_HP_PEOPLE_EXTEND_PRIV_V1: BuiltinGroup = BuiltinGroup { + name: "idm_hp_people_extend_priv", + description: "Builtin IDM Group for extending high privilege accounts to be people.", + uuid: UUID_IDM_HP_PEOPLE_EXTEND_PRIV, + members: vec![UUID_SYSTEM_ADMINS], + ..Default::default() + }; + +// * group write manager (no read, everyone has read via the anon, etc) + + /// Builtin IDM Group for granting elevated group write and lifecycle permissions. + pub static ref IDM_GROUP_MANAGE_PRIV_V1: BuiltinGroup = BuiltinGroup { + name: "idm_group_manage_priv", + description: "Builtin IDM Group for granting elevated group write and lifecycle permissions.", + uuid: UUID_IDM_GROUP_MANAGE_PRIV, + members: vec![ + BUILTIN_GROUP_IDM_ADMINS_V1.uuid, + BUILTIN_GROUP_SYSTEM_ADMINS_V1.uuid, + ], + ..Default::default() + }; + + /// Builtin IDM Group for granting elevated group write and lifecycle permissions. + pub static ref IDM_GROUP_WRITE_PRIV_V1: BuiltinGroup = BuiltinGroup { + name: "idm_group_write_priv", + description: "Builtin IDM Group for granting elevated group write permissions.", + uuid: UUID_IDM_GROUP_WRITE_PRIV, + members: vec![ + UUID_IDM_GROUP_MANAGE_PRIV + ], + ..Default::default() + }; + + /// Builtin IDM Group for granting unix group extension permissions. + pub static ref IDM_GROUP_UNIX_EXTEND_PRIV_V1: BuiltinGroup = BuiltinGroup { + name: "idm_group_unix_extend_priv", + description: "Builtin IDM Group for granting UNIX group extension permissions.", + uuid: UUID_IDM_GROUP_UNIX_EXTEND_PRIV, + members: vec![ + UUID_IDM_ADMINS + ], + ..Default::default() + }; + + /// Account read manager + pub static ref IDM_ACCOUNT_READ_PRIV_V1: BuiltinGroup = BuiltinGroup { + name: "idm_account_read_priv", + description: "Builtin IDM Group for granting elevated account read permissions.", + uuid: UUID_IDM_ACCOUNT_READ_PRIV, + members: vec![ + UUID_IDM_ACCOUNT_WRITE_PRIV, + ], + ..Default::default() + }; + + pub static ref IDM_ACCOUNT_MANAGE_PRIV_V1: BuiltinGroup = BuiltinGroup { + name: "idm_account_manage_priv", + description: "Builtin IDM Group for granting elevated account write and lifecycle permissions.", + uuid: UUID_IDM_ACCOUNT_MANAGE_PRIV, + members: vec![ + UUID_IDM_ADMINS, + ], + ..Default::default() + }; + + pub static ref IDM_ACCOUNT_WRITE_PRIV_V1: BuiltinGroup = BuiltinGroup { + name: "idm_account_write_priv", + description: "Builtin IDM Group for granting elevated account write permissions.", + uuid: UUID_IDM_ACCOUNT_WRITE_PRIV, + members: vec![ + UUID_IDM_ACCOUNT_MANAGE_PRIV, + ], + ..Default::default() + }; + + pub static ref IDM_ACCOUNT_UNIX_EXTEND_PRIV_V1: BuiltinGroup = BuiltinGroup { + name: "idm_account_unix_extend_priv", + description: "Builtin IDM Group for granting account unix extend permissions.", + uuid: UUID_IDM_ACCOUNT_UNIX_EXTEND_PRIV, + members: vec![ + UUID_IDM_ADMINS, + ], + ..Default::default() + }; + + /// Builtin IDM Group for RADIUS secret write for all non-hp accounts. + pub static ref IDM_RADIUS_SECRET_WRITE_PRIV_V1: BuiltinGroup = BuiltinGroup { + name: "idm_radius_secret_write_priv", + description: "Builtin IDM Group for RADIUS secret write for all non-hp accounts.", + uuid: UUID_IDM_RADIUS_SECRET_WRITE_PRIV_V1, + members: vec![ + UUID_IDM_ADMINS, + ], + ..Default::default() + }; + + /// Builtin IDM Group for RADIUS secret reading for all non-hp accounts. + pub static ref IDM_RADIUS_SECRET_READ_PRIV_V1: BuiltinGroup = BuiltinGroup { + name: "idm_radius_secret_read_priv", + description: "Builtin IDM Group for RADIUS secret reading for all non-hp accounts.", + uuid: UUID_IDM_RADIUS_SECRET_READ_PRIV_V1, + members: vec![ + UUID_IDM_RADIUS_SECRET_WRITE_PRIV_V1, + ], + ..Default::default() + }; + + /// Builtin IDM Group for RADIUS server access delegation. + pub static ref IDM_RADIUS_SERVERS_V1: BuiltinGroup = BuiltinGroup { + name: "idm_radius_servers", + description: "Builtin IDM Group for RADIUS server access delegation.", + uuid: UUID_IDM_RADIUS_SERVERS, + members: vec![ + ], + ..Default::default() + }; + + /// High privilege account read manager + pub static ref IDM_HP_ACCOUNT_READ_PRIV_V1: BuiltinGroup = BuiltinGroup { + name: "idm_hp_account_read_priv", + description: "Builtin IDM Group for granting elevated account read permissions over high privilege accounts.", + uuid: UUID_IDM_HP_ACCOUNT_READ_PRIV, + members: vec![ + UUID_IDM_HP_ACCOUNT_WRITE_PRIV + ], + ..Default::default() + }; + + /// Builtin IDM Group for granting elevated account write permissions over high privilege accounts. + pub static ref IDM_HP_ACCOUNT_MANAGE_PRIV_V1: BuiltinGroup = BuiltinGroup { + name: "idm_hp_account_manage_priv", + description: "Builtin IDM Group for granting elevated account write and lifecycle permissions over high privilege accounts.", + uuid: UUID_IDM_HP_ACCOUNT_MANAGE_PRIV, + members: vec![ + UUID_SYSTEM_ADMINS, + ], + ..Default::default() + }; + /// Builtin IDM Group for granting elevated account write permissions over high privilege accounts. + pub static ref IDM_HP_ACCOUNT_WRITE_PRIV_V1: BuiltinGroup = BuiltinGroup { + name: "idm_hp_account_write_priv", + description: "Builtin IDM Group for granting elevated account write permissions over high privilege accounts.", + uuid: UUID_IDM_HP_ACCOUNT_WRITE_PRIV, + members: vec![ + UUID_IDM_HP_ACCOUNT_MANAGE_PRIV, + ], + ..Default::default() + }; + + /// Builtin IDM Group for granting account unix extend permissions for high privilege accounts. + pub static ref IDM_HP_ACCOUNT_UNIX_EXTEND_PRIV_V1: BuiltinGroup = BuiltinGroup { + name: "idm_hp_account_unix_extend_priv", + description: "Builtin IDM Group for granting account UNIX extend permissions for high privilege accounts.", + uuid: UUID_IDM_HP_ACCOUNT_UNIX_EXTEND_PRIV, + members: vec![ + UUID_SYSTEM_ADMINS, + ], + ..Default::default() + }; + + /// * Schema write manager + pub static ref IDM_SCHEMA_MANAGE_PRIV_V1: BuiltinGroup = BuiltinGroup { + name: "idm_schema_manage_priv", + description: "Builtin IDM Group for granting elevated schema write and management permissions.", + uuid: UUID_IDM_SCHEMA_MANAGE_PRIV, + members: vec![ + UUID_SYSTEM_ADMINS, + ], + ..Default::default() + }; + + /// ACP read/write manager + pub static ref IDM_ACP_MANAGE_PRIV_V1: BuiltinGroup = BuiltinGroup { + name: "idm_acp_manage_priv", + description: "Builtin IDM Group for granting control over all access control profile modifications.", + uuid: UUID_IDM_ACP_MANAGE_PRIV, + members: vec![ + UUID_SYSTEM_ADMINS, + ], + ..Default::default() + }; + + /// Builtin IDM Group for granting elevated group write and lifecycle privileges for high privilege groups. + pub static ref IDM_HP_GROUP_MANAGE_PRIV_V1: BuiltinGroup = BuiltinGroup { + name: "idm_hp_group_manage_priv", + description: "Builtin IDM Group for granting elevated group write and lifecycle privileges for high privilege groups.", + uuid: UUID_IDM_HP_GROUP_MANAGE_PRIV, + members: vec![ + UUID_SYSTEM_ADMINS, + ], + ..Default::default() + }; + + /// Builtin IDM Group for granting elevated group write privileges for high privilege groups. + pub static ref IDM_HP_GROUP_WRITE_PRIV_V1: BuiltinGroup = BuiltinGroup { + name: "idm_hp_group_write_priv", + description: "Builtin IDM Group for granting elevated group write privileges for high privilege groups.", + uuid: UUID_IDM_HP_GROUP_WRITE_PRIV, + members: vec![ + UUID_IDM_HP_GROUP_MANAGE_PRIV, + ], + ..Default::default() + }; + +} +// at some point vs code just gives up on syntax highlighting inside lazy_static... +lazy_static! { + + /// Builtin IDM Group for granting unix group extension permissions for high privilege groups. + pub static ref IDM_HP_GROUP_UNIX_EXTEND_PRIV_V1: BuiltinGroup = BuiltinGroup { + name: "idm_hp_group_unix_extend_priv", + description: "Builtin IDM Group for granting unix group extension permissions for high privilege groups.", + uuid: UUID_IDM_HP_GROUP_UNIX_EXTEND_PRIV, + members: vec![ + UUID_SYSTEM_ADMINS, + ], + ..Default::default() + }; + + /// Builtin IDM Group for granting local domain administration rights and trust administration rights + pub static ref DOMAIN_ADMINS: BuiltinGroup = BuiltinGroup { + name: "domain_admins", + description: "Builtin IDM Group for granting local domain administration rights and trust administration rights.", + uuid: UUID_DOMAIN_ADMINS, + members: vec![ + UUID_ADMIN, + ], + ..Default::default() + }; + + + /// Builtin IDM Group for managing oauth2 resource server integrations to this authentication domain. + pub static ref IDM_HP_OAUTH2_MANAGE_PRIV_V1: BuiltinGroup = BuiltinGroup { + name: "idm_hp_oauth2_manage_priv", + description: "Builtin IDM Group for managing oauth2 resource server integrations to this authentication domain.", + uuid: UUID_IDM_HP_OAUTH2_MANAGE_PRIV, + members: vec![ + UUID_SYSTEM_ADMINS, + ], + ..Default::default() + }; + + /// Builtin IDM Group for allowing migrations of service accounts into persons + pub static ref IDM_HP_SERVICE_ACCOUNT_INTO_PERSON_MIGRATE_PRIV: BuiltinGroup = BuiltinGroup { + name: "idm_hp_service_account_into_person_migrate_priv", + description:"Builtin IDM Group for allowing migrations of service accounts into persons", + uuid: UUID_IDM_HP_SERVICE_ACCOUNT_INTO_PERSON_MIGRATE_PRIV, + members: vec![ + UUID_SYSTEM_ADMINS, + ], + ..Default::default() + }; + + + /// Builtin IDM Group for allowing migrations of service accounts into persons + pub static ref IDM_HP_SYNC_ACCOUNT_MANAGE_PRIV: BuiltinGroup = BuiltinGroup { + name: "idm_hp_sync_account_manage_priv", + description: "Builtin IDM Group for managing synchronisation from external identity sources", + uuid: UUID_IDM_HP_SYNC_ACCOUNT_MANAGE_PRIV, + members: vec![ + UUID_SYSTEM_ADMINS, + ], + ..Default::default() + }; + + /// Builtin IDM Group for extending high privilege accounts to be people. + pub static ref IDM_ALL_PERSONS: BuiltinGroup = BuiltinGroup { + name: "idm_all_persons", + description: "Builtin IDM Group for extending high privilege accounts to be people.", + uuid: UUID_IDM_ALL_PERSONS, + members: Vec::new(), + dyngroup: true, + dyngroup_filter: Some( + Filter::And(vec![ + Filter::Eq(Attribute::Class.to_string(), EntryClass::Person.to_string()), + Filter::Eq(Attribute::Class.to_string(), EntryClass::Account.to_string()), + ]) + ), + ..Default::default() + }; + + /// Builtin IDM Group for extending high privilege accounts to be people. + pub static ref IDM_ALL_ACCOUNTS: BuiltinGroup = BuiltinGroup { + name: "idm_all_accounts", + description: "Builtin IDM dynamic group containing all entries that can authenticate.", + uuid: UUID_IDM_ALL_ACCOUNTS, + members: Vec::new(), + dyngroup: true, + dyngroup_filter: Some( + Filter::Eq(Attribute::Class.to_string(), EntryClass::Account.to_string()), + ), + ..Default::default() + }; + + + pub static ref IDM_UI_ENABLE_EXPERIMENTAL_FEATURES: BuiltinGroup = BuiltinGroup { + name: "idm_ui_enable_experimental_features", + description: "Members of this group will have access to experimental web UI features.", + uuid: UUID_IDM_UI_ENABLE_EXPERIMENTAL_FEATURES, + extra_attributes: vec![ + (Attribute::GrantUiHint, Value::UiHint(UiHint::ExperimentalFeatures)) + ], + ..Default::default() + }; + + /// Members of this group will have access to read the mail attribute of all persons and service accounts. + pub static ref IDM_ACCOUNT_MAIL_READ_PRIV: BuiltinGroup = BuiltinGroup { + name: "idm_account_mail_read_priv", + description: "Members of this group will have access to read the mail attribute of all persons and service accounts.", + uuid: UUID_IDM_ACCOUNT_MAIL_READ_PRIV, + ..Default::default() + }; + + /// This must be the last group to init to include the UUID of the other high priv groups. + pub static ref IDM_HIGH_PRIVILEGE_V1: BuiltinGroup = BuiltinGroup { + name: "idm_high_privilege", + uuid: UUID_IDM_HIGH_PRIVILEGE, + description: "Builtin IDM provided groups with high levels of access that should be audited and limited in modification.", + members: vec![ + UUID_IDM_ADMINS, + UUID_IDM_PEOPLE_READ_PRIV, + UUID_IDM_PEOPLE_WRITE_PRIV, + UUID_IDM_GROUP_WRITE_PRIV, + UUID_IDM_ACCOUNT_READ_PRIV, + UUID_IDM_ACCOUNT_WRITE_PRIV, + UUID_IDM_RADIUS_SERVERS, + UUID_IDM_HP_ACCOUNT_READ_PRIV, + UUID_IDM_HP_ACCOUNT_WRITE_PRIV, + UUID_IDM_SCHEMA_MANAGE_PRIV, + UUID_IDM_ACP_MANAGE_PRIV, + UUID_IDM_HP_GROUP_WRITE_PRIV, + UUID_IDM_PEOPLE_MANAGE_PRIV, + UUID_IDM_ACCOUNT_MANAGE_PRIV, + UUID_IDM_GROUP_MANAGE_PRIV, + UUID_IDM_HP_ACCOUNT_MANAGE_PRIV, + UUID_IDM_HP_GROUP_MANAGE_PRIV, + UUID_SYSTEM_ADMINS, + UUID_DOMAIN_ADMINS, + UUID_IDM_PEOPLE_ACCOUNT_PASSWORD_IMPORT_PRIV, + UUID_IDM_PEOPLE_EXTEND_PRIV, + UUID_IDM_HP_ACCOUNT_UNIX_EXTEND_PRIV, + UUID_IDM_HP_GROUP_UNIX_EXTEND_PRIV, + UUID_IDM_HP_OAUTH2_MANAGE_PRIV, + UUID_IDM_RADIUS_SECRET_WRITE_PRIV_V1, + UUID_IDM_RADIUS_SECRET_READ_PRIV_V1, + UUID_IDM_HP_SERVICE_ACCOUNT_INTO_PERSON_MIGRATE_PRIV, + UUID_IDM_HP_SYNC_ACCOUNT_MANAGE_PRIV, + UUID_IDM_HIGH_PRIVILEGE, + ], + dyngroup: false, + dyngroup_filter: None, + extra_attributes: Vec::new(), + }; +} + +/// Make a list of all the non-admin BuiltinGroup's that are created by default, doing it in a standard-ish way so we can use it for testing and stuff +pub fn idm_builtin_non_admin_groups() -> Vec<&'static BuiltinGroup> { + // Create any system default schema entries. + vec![ + &IDM_ALL_PERSONS, + &IDM_ALL_ACCOUNTS, + &IDM_PEOPLE_MANAGE_PRIV_V1, + &IDM_PEOPLE_ACCOUNT_PASSWORD_IMPORT_PRIV_V1, + &IDM_PEOPLE_EXTEND_PRIV_V1, + &IDM_PEOPLE_SELF_WRITE_MAIL_PRIV_V1, + &IDM_PEOPLE_WRITE_PRIV_V1, + &IDM_PEOPLE_READ_PRIV_V1, + &IDM_HP_PEOPLE_EXTEND_PRIV_V1, + &IDM_HP_PEOPLE_WRITE_PRIV_V1, + &IDM_HP_PEOPLE_READ_PRIV_V1, + &IDM_GROUP_MANAGE_PRIV_V1, + &IDM_GROUP_WRITE_PRIV_V1, + &IDM_GROUP_UNIX_EXTEND_PRIV_V1, + &IDM_ACCOUNT_MANAGE_PRIV_V1, + &IDM_ACCOUNT_WRITE_PRIV_V1, + &IDM_ACCOUNT_UNIX_EXTEND_PRIV_V1, + &IDM_ACCOUNT_READ_PRIV_V1, + &IDM_RADIUS_SECRET_WRITE_PRIV_V1, + &IDM_RADIUS_SECRET_READ_PRIV_V1, + &IDM_RADIUS_SERVERS_V1, + // Write deps on read, so write must be added first. + &IDM_HP_ACCOUNT_MANAGE_PRIV_V1, + &IDM_HP_ACCOUNT_WRITE_PRIV_V1, + &IDM_HP_ACCOUNT_READ_PRIV_V1, + &IDM_HP_ACCOUNT_UNIX_EXTEND_PRIV_V1, + &IDM_SCHEMA_MANAGE_PRIV_V1, + &IDM_HP_GROUP_MANAGE_PRIV_V1, + &IDM_HP_GROUP_WRITE_PRIV_V1, + &IDM_HP_GROUP_UNIX_EXTEND_PRIV_V1, + &IDM_ACP_MANAGE_PRIV_V1, + &DOMAIN_ADMINS, + &IDM_HP_OAUTH2_MANAGE_PRIV_V1, + &IDM_HP_SERVICE_ACCOUNT_INTO_PERSON_MIGRATE_PRIV, + &IDM_HP_SYNC_ACCOUNT_MANAGE_PRIV, + // All members must exist before we write HP + &IDM_HIGH_PRIVILEGE_V1, + // other things + &IDM_UI_ENABLE_EXPERIMENTAL_FEATURES, + &IDM_ACCOUNT_MAIL_READ_PRIV, + ] +} + +pub fn idm_builtin_admin_groups() -> Vec<&'static BuiltinGroup> { + vec![ + &BUILTIN_GROUP_SYSTEM_ADMINS_V1, + &BUILTIN_GROUP_IDM_ADMINS_V1, + ] +} diff --git a/server/lib/src/constants/mod.rs b/server/lib/src/constants/mod.rs index 086d7a1a9..d5cba292f 100644 --- a/server/lib/src/constants/mod.rs +++ b/server/lib/src/constants/mod.rs @@ -2,6 +2,7 @@ pub mod acp; pub mod entries; +pub mod groups; pub mod schema; pub mod system_config; pub mod uuids; @@ -9,6 +10,7 @@ pub mod values; pub use crate::constants::acp::*; pub use crate::constants::entries::*; +pub use crate::constants::groups::*; pub use crate::constants::schema::*; pub use crate::constants::system_config::*; pub use crate::constants::uuids::*; diff --git a/server/lib/src/entry.rs b/server/lib/src/entry.rs index 0cd3f975a..64311a4db 100644 --- a/server/lib/src/entry.rs +++ b/server/lib/src/entry.rs @@ -351,6 +351,7 @@ impl Entry { } #[cfg(test)] + // TODO: #[deprecated(note = "Use entry_init! macro instead or like... anything else")] pub(crate) fn unsafe_from_entry_str(es: &str) -> Self { // Just use log directly here, it's testing // str -> proto entry diff --git a/server/lib/src/server/access/mod.rs b/server/lib/src/server/access/mod.rs index 77803031b..963206996 100644 --- a/server/lib/src/server/access/mod.rs +++ b/server/lib/src/server/access/mod.rs @@ -2358,8 +2358,7 @@ mod tests { let admin = Identity::from_impersonate_entry_readwrite(E_TEST_ACCOUNT_1.clone()); - let e1: Entry = Entry::unsafe_from_entry_str(JSON_TESTPERSON1); - let ev1 = e1.into_sealed_committed(); + let ev1 = E_TESTPERSON_1.clone().into_sealed_committed(); let r_set = vec![Arc::new(ev1)]; @@ -2398,8 +2397,7 @@ mod tests { let admin = Identity::from_impersonate_entry_readwrite(E_TEST_ACCOUNT_1.clone()); - let e1: Entry = Entry::unsafe_from_entry_str(JSON_TESTPERSON1); - let ev1 = e1.into_sealed_committed(); + let ev1 = E_TESTPERSON_1.clone().into_sealed_committed(); let r_set = vec![Arc::new(ev1)]; diff --git a/server/lib/src/server/migrations.rs b/server/lib/src/server/migrations.rs index 165c2a70d..355ea34c6 100644 --- a/server/lib/src/server/migrations.rs +++ b/server/lib/src/server/migrations.rs @@ -615,13 +615,7 @@ impl<'a> QueryServerWriteTransaction<'a> { // Check the admin object exists (migrations). // Create the default idm_admin group. - let admin_entries: Vec = vec![ - BUILTIN_ACCOUNT_ANONYMOUS_V1.clone().into(), - BUILTIN_ACCOUNT_ADMIN.clone().into(), - BUILTIN_ACCOUNT_IDM_ADMIN.clone().into(), - BUILTIN_GROUP_IDM_ADMINS_V1.clone().try_into()?, - BUILTIN_GROUP_SYSTEM_ADMINS_V1.clone().try_into()?, - ]; + let admin_entries: Vec = idm_builtin_admin_entries()?; let res: Result<(), _> = admin_entries .into_iter() // Each item individually logs it's result @@ -632,49 +626,7 @@ impl<'a> QueryServerWriteTransaction<'a> { debug_assert!(res.is_ok()); res?; - // Create any system default schema entries. - let idm_entries: Vec<&BuiltinGroup> = vec![ - &IDM_ALL_PERSONS, - &IDM_ALL_ACCOUNTS, - &IDM_PEOPLE_MANAGE_PRIV_V1, - &IDM_PEOPLE_ACCOUNT_PASSWORD_IMPORT_PRIV_V1, - &IDM_PEOPLE_EXTEND_PRIV_V1, - &IDM_PEOPLE_SELF_WRITE_MAIL_PRIV_V1, - &IDM_PEOPLE_WRITE_PRIV_V1, - &IDM_PEOPLE_READ_PRIV_V1, - &IDM_HP_PEOPLE_EXTEND_PRIV_V1, - &IDM_HP_PEOPLE_WRITE_PRIV_V1, - &IDM_HP_PEOPLE_READ_PRIV_V1, - &IDM_GROUP_MANAGE_PRIV_V1, - &IDM_GROUP_WRITE_PRIV_V1, - &IDM_GROUP_UNIX_EXTEND_PRIV_V1, - &IDM_ACCOUNT_MANAGE_PRIV_V1, - &IDM_ACCOUNT_WRITE_PRIV_V1, - &IDM_ACCOUNT_UNIX_EXTEND_PRIV_V1, - &IDM_ACCOUNT_READ_PRIV_V1, - &IDM_RADIUS_SECRET_WRITE_PRIV_V1, - &IDM_RADIUS_SECRET_READ_PRIV_V1, - &IDM_RADIUS_SERVERS_V1, - // Write deps on read, so write must be added first. - &IDM_HP_ACCOUNT_MANAGE_PRIV_V1, - &IDM_HP_ACCOUNT_WRITE_PRIV_V1, - &IDM_HP_ACCOUNT_READ_PRIV_V1, - &IDM_HP_ACCOUNT_UNIX_EXTEND_PRIV_V1, - &IDM_SCHEMA_MANAGE_PRIV_V1, - &IDM_HP_GROUP_MANAGE_PRIV_V1, - &IDM_HP_GROUP_WRITE_PRIV_V1, - &IDM_HP_GROUP_UNIX_EXTEND_PRIV_V1, - &IDM_ACP_MANAGE_PRIV_V1, - &DOMAIN_ADMINS, - &IDM_HP_OAUTH2_MANAGE_PRIV_V1, - &IDM_HP_SERVICE_ACCOUNT_INTO_PERSON_MIGRATE_PRIV, - &IDM_HP_SYNC_ACCOUNT_MANAGE_PRIV, - // All members must exist before we write HP - &IDM_HIGH_PRIVILEGE_V1, - // other things - &IDM_UI_ENABLE_EXPERIMENTAL_FEATURES, - &IDM_ACCOUNT_MAIL_READ_PRIV, - ]; + let idm_entries = idm_builtin_non_admin_groups(); let res: Result<(), _> = idm_entries .into_iter() diff --git a/server/lib/src/server/mod.rs b/server/lib/src/server/mod.rs index 129c8d3f9..7a45c15f1 100644 --- a/server/lib/src/server/mod.rs +++ b/server/lib/src/server/mod.rs @@ -1138,9 +1138,9 @@ impl<'a> QueryServerTransaction<'a> for QueryServerWriteTransaction<'a> { impl QueryServer { pub fn new(be: Backend, schema: Schema, domain_name: String) -> Result { let (s_uuid, d_uuid) = { - let mut wr = be.write().unwrap(); - let s_uuid = wr.get_db_s_uuid().unwrap(); - let d_uuid = wr.get_db_d_uuid().unwrap(); + let mut wr = be.write()?; + let s_uuid = wr.get_db_s_uuid()?; + let d_uuid = wr.get_db_d_uuid()?; #[allow(clippy::expect_used)] wr.commit() .expect("Critical - unable to commit db_s_uuid or db_d_uuid"); diff --git a/server/testkit/Cargo.toml b/server/testkit/Cargo.toml index c9926b417..faf4cbd67 100644 --- a/server/testkit/Cargo.toml +++ b/server/testkit/Cargo.toml @@ -39,6 +39,8 @@ tracing = { workspace = true, features = ["attributes"] } tokio = { workspace = true, features = ["net", "sync", "io-util", "macros"] } openssl = { workspace = true } lazy_static = { workspace = true } +petgraph = { version = "0.6.4", features = ["serde"] } +regex.workspace = true [build-dependencies] diff --git a/server/testkit/examples/enumerating_access.rs b/server/testkit/examples/enumerating_access.rs new file mode 100644 index 000000000..bfa7d0afc --- /dev/null +++ b/server/testkit/examples/enumerating_access.rs @@ -0,0 +1,119 @@ +//! I'm working towards making this a proper enumeration/discovery toolkit for access things in Kanidm. +//! +//! - @yaleman +//! + +use std::collections::{BTreeSet, HashMap}; +// use kanidm_client::KanidmClient; +use kanidmd_lib::constants::entries::Attribute; +use kanidmd_lib::constants::groups::{idm_builtin_admin_groups, idm_builtin_non_admin_groups}; +use kanidmd_lib::prelude::{builtin_accounts, EntryInitNew}; +use petgraph::graphmap::GraphMap; +use uuid::Uuid; + +async fn enumerate_default_groups(/*_client: KanidmClient*/) { + let mut uuidmap: HashMap = HashMap::new(); + + let mut graph = GraphMap::::new(); + + builtin_accounts().into_iter().for_each(|account| { + // println!("adding builtin {}", account.uuid); + uuidmap.insert(account.uuid, account.clone().try_into().unwrap()); + graph.add_node(account.uuid); + }); + + idm_builtin_non_admin_groups() + .into_iter() + .for_each(|group| { + uuidmap.insert(group.uuid, group.clone().try_into().unwrap()); + graph.add_node(group.uuid); + + group.members.iter().for_each(|member| { + graph.add_edge(*member, group.uuid, ()); + }); + }); + + idm_builtin_admin_groups().into_iter().for_each(|group| { + uuidmap.insert(group.uuid, group.clone().try_into().unwrap()); + graph.add_node(group.uuid); + + group.members.iter().for_each(|member| { + graph.add_edge(*member, group.uuid, ()); + }); + }); + + // // println!("{}", mermaidchart); + // let mut dotgraph = format!("{:?}", Dot::with_config(&graph, &[Config::EdgeNoLabel])); + // // regex to extract uuids + // // let re = regex::Regex::new(r"(\w{8}-\w{4}-\w{4}-\w{4}-\w{12})").unwrap(); + // for (uuid, uuid_value) in uuidmap.clone() { + // let uuid_str = uuid.to_string(); + // if dotgraph.contains(&uuid_str) { + // // println!("uuid {} not found in graph", uuid_str); + // let name = uuid_value.get_ava_single(Attribute::Name).unwrap(); + // dotgraph = dotgraph.replace(&uuid_str, name.as_string().unwrap()); + // } + // } + // // println!("{}", dotgraph); + + #[derive(Debug)] + enum EntryType { + Person(String), + ServiceAccount(String), + Group(String), + UnknownType(String), + } + + impl EntryType { + fn as_mermaid_tag(&self) -> String { + match self { + EntryType::Person(name) => format!("{}(\"Person:{}\")", name, name), + EntryType::ServiceAccount(name) => format!("{}{{\"SA: {}\"}}", name, name), + EntryType::Group(name) => format!("{}[\"Group: {}\"]", name, name), + EntryType::UnknownType(name) => format!("{}[\"Unknown Type {}\"]", name, name), + } + } + } + + impl From for EntryType { + fn from(entry: EntryInitNew) -> Self { + let name = entry.get_ava_single(Attribute::Name).unwrap(); + let name = name.as_string().unwrap(); + let classes = entry + .get_ava_set(Attribute::Class) + .unwrap() + .as_iutf8_set() + .cloned() + .unwrap_or(BTreeSet::::new()); + if classes.contains("group") { + EntryType::Group(name.clone()) + } else if classes.contains("service_account") { + EntryType::ServiceAccount(name.clone()) + } else if classes.contains("person") { + EntryType::Person(name.clone()) + } else { + EntryType::UnknownType(name.clone()) + } + } + } + + println!("graph RL;"); + for (left, right, _weight) in graph.all_edges() { + let left = uuidmap.get(&left).unwrap(); + // let left_name = left.get_ava_single(Attribute::Name).unwrap(); + + let right = uuidmap.get(&right).unwrap(); + // let right_name = right.get_ava_single(Attribute::Name).unwrap(); + + println!( + " {} --> {}", + EntryType::from(left.clone()).as_mermaid_tag(), + EntryType::from(right.clone()).as_mermaid_tag(), + ); + } +} + +#[tokio::main] +async fn main() { + enumerate_default_groups().await; +} diff --git a/tools/cli/Cargo.toml b/tools/cli/Cargo.toml index 31fd71f89..e221c03b8 100644 --- a/tools/cli/Cargo.toml +++ b/tools/cli/Cargo.toml @@ -3,7 +3,6 @@ name = "kanidm_tools" default-run = "kanidm" description = "Kanidm Client Tools" documentation = "https://kanidm.github.io/kanidm/stable/" - version = { workspace = true } authors = { workspace = true } rust-version = { workspace = true } @@ -52,7 +51,8 @@ url = { workspace = true, features = ["serde"] } uuid = { workspace = true } zxcvbn = { workspace = true } lazy_static.workspace = true -regex.workspace = true +regex = { workspace = true } +sketching = { workspace = true } [dependencies.cursive] version = "0.20.0" diff --git a/tools/cli/src/opt/ssh_authorizedkeys.rs b/tools/cli/src/opt/ssh_authorizedkeys.rs index 947973fb1..8f51a7ab6 100644 --- a/tools/cli/src/opt/ssh_authorizedkeys.rs +++ b/tools/cli/src/opt/ssh_authorizedkeys.rs @@ -1,6 +1,6 @@ #[derive(Debug, Parser)] #[command(version)] -struct SshAuthorizedOpt { +pub(crate) struct SshAuthorizedOpt { #[clap(short, long = "debug")] debug: bool, #[clap(short = 'H', long = "url")] diff --git a/tools/cli/src/ssh_authorizedkeys.rs b/tools/cli/src/ssh_authorizedkeys.rs index 028696db1..9d519325b 100644 --- a/tools/cli/src/ssh_authorizedkeys.rs +++ b/tools/cli/src/ssh_authorizedkeys.rs @@ -11,23 +11,20 @@ use std::path::PathBuf; use clap::Parser; -use kanidm_client::{ClientError, KanidmClientBuilder}; +use kanidm_client::{ClientError, KanidmClient, KanidmClientBuilder}; use kanidm_proto::constants::{DEFAULT_CLIENT_CONFIG_PATH, DEFAULT_CLIENT_CONFIG_PATH_HOME}; use tracing::{debug, error}; include!("opt/ssh_authorizedkeys.rs"); -// For now we lift a few things from the main.rs to use. -// -// usage: AuthorizedKeysCommand /usr/sbin/kanidm_ssh_authorizedkeys %u -H URL -D anonymous -C /etc/kanidm/ca.pem -// -#[tokio::main(flavor = "current_thread")] -async fn main() { - let opt = SshAuthorizedOpt::parse(); +pub(crate) fn build_configured_client(opt: &SshAuthorizedOpt) -> Result { if opt.debug { ::std::env::set_var("RUST_LOG", "kanidm=debug,kanidm_client=debug"); } + #[cfg(not(test))] tracing_subscriber::fmt::init(); + #[cfg(test)] + sketching::test_init(); let config_path: String = shellexpand::tilde(DEFAULT_CLIENT_CONFIG_PATH_HOME).into_owned(); debug!("Attempting to use config {}", DEFAULT_CLIENT_CONFIG_PATH); @@ -37,10 +34,9 @@ async fn main() { debug!("Attempting to use config {}", config_path); cb.read_options_from_optional_config(config_path) }) - .unwrap_or_else(|e| { + .map_err(|e| { error!("Failed to parse config (if present) -- {:?}", e); - std::process::exit(1); - }); + })?; let client_builder = match &opt.addr { Some(a) => client_builder.address(a.to_string()), @@ -51,25 +47,31 @@ async fn main() { let client_builder = match ca_path { Some(p) => client_builder .add_root_certificate_filepath(p) - .unwrap_or_else(|e| { + .map_err(|e| { error!("Failed to add ca certificate -- {:?}", e); - std::process::exit(1); - }), + })?, None => client_builder, }; - let client = client_builder.build().unwrap_or_else(|e| { - error!("Failed to build client instance -- {:?}", e); - std::process::exit(1); - }); + client_builder + .build() + .map_err(|e| error!("Failed to build client instance -- {:?}", e)) +} + +// For now we lift a few things from the main.rs to use. +// +// usage: AuthorizedKeysCommand /usr/sbin/kanidm_ssh_authorizedkeys %u -H URL -D anonymous -C /etc/kanidm/ca.pem +// +#[tokio::main(flavor = "current_thread")] +async fn main() -> Result<(), ()> { + let opt: SshAuthorizedOpt = SshAuthorizedOpt::parse(); + let client = build_configured_client(&opt)?; let r = if opt.username == "anonymous" { client.auth_anonymous().await } else { - let password = rpassword::prompt_password("Enter password: ").unwrap_or_else(|e| { - error!("Failed to retrieve password - {:?}", e); - std::process::exit(1); - }); + let password = rpassword::prompt_password("Enter password: ") + .map_err(|e| error!("Failed to retrieve password - {:?}", e))?; client .auth_simple_password(opt.username.as_str(), password.as_str()) .await @@ -81,16 +83,52 @@ async fn main() { } _ => error!("Error during authentication phase: {:?}", r), } - std::process::exit(1); + return Err(()); } - match client + client .idm_account_get_ssh_pubkeys(opt.account_id.as_str()) .await - { - Ok(pkeys) => pkeys.iter().for_each(|pkey| println!("{}", pkey)), - Err(e) => { - error!("Failed to retrieve pubkeys - {:?}", e); - } + .map(|pkeys| pkeys.iter().for_each(|pkey| println!("{}", pkey))) + .map_err(|e| { + error!( + "Failed to retrieve SSH keys for {} - {:?}", + opt.account_id.to_string(), + e + ) + }) +} + +#[cfg(test)] +mod tests { + + use std::path::PathBuf; + + use crate::build_configured_client; + use crate::SshAuthorizedOpt; + #[test] + fn test_build_configured_client() { + let opt = SshAuthorizedOpt { + debug: false, + addr: Some("https://example.com:8443".to_string()), + ca_path: None, + username: "anonymous".to_string(), + account_id: "anonymous".to_string(), + }; + let client = build_configured_client(&opt); + assert!(client.is_ok()); + } + + #[test] + fn test_build_configured_client_err() { + let opt = SshAuthorizedOpt { + debug: false, + addr: None, + ca_path: Some(PathBuf::from("/etc/kanidm/ca.pem")), + username: "anonymous".to_string(), + account_id: "anonymous".to_string(), + }; + let client = build_configured_client(&opt); + assert!(client.is_err()) } }