diff --git a/server/lib/src/entry.rs b/server/lib/src/entry.rs index 58caa8458..11611efa7 100644 --- a/server/lib/src/entry.rs +++ b/server/lib/src/entry.rs @@ -166,6 +166,15 @@ pub struct EntryReduced { // Today is that day - @firstyear pub type Eattrs = Map; +pub trait GetUuid { + fn get_uuid(&self) -> Uuid; +} + +pub trait Committed {} + +impl Committed for EntrySealed {} +impl Committed for EntryReduced {} + pub(crate) fn compare_attrs(left: &Eattrs, right: &Eattrs) -> bool { // We can't shortcut based on len because cid mod may not be present. // Build the set of all keys between both. @@ -260,7 +269,7 @@ where STATE: Clone, { /// Get the uuid of this entry. - pub(crate) fn get_uuid(&self) -> Option { + pub fn get_uuid(&self) -> Option { self.attrs .get(&Attribute::Uuid) .and_then(|vs| vs.to_uuid_single()) @@ -2127,6 +2136,15 @@ impl Entry { } } +impl GetUuid for Entry +where + STATE: Clone, +{ + fn get_uuid(&self) -> Uuid { + self.valid.uuid + } +} + impl Entry where STATE: Clone, @@ -2213,6 +2231,12 @@ where } } +impl GetUuid for Entry { + fn get_uuid(&self) -> Uuid { + self.valid.uuid + } +} + impl Entry { pub fn get_uuid(&self) -> Uuid { self.valid.uuid diff --git a/server/lib/src/idm/account.rs b/server/lib/src/idm/account.rs index bd62bb475..6616cddfa 100644 --- a/server/lib/src/idm/account.rs +++ b/server/lib/src/idm/account.rs @@ -12,10 +12,7 @@ use webauthn_rs::prelude::{ }; use super::accountpolicy::ResolvedAccountPolicy; -use super::group::{ - load_all_groups_from_account_entry, load_all_groups_from_account_entry_reduced, - load_all_groups_from_account_entry_with_policy, Group, UnixGroup, -}; +use super::group::{load_account_policy, load_all_groups_from_account, Group, Unix}; use crate::constants::UUID_ANONYMOUS; use crate::credential::softlock::CredSoftLockPolicy; use crate::credential::{apppwd::ApplicationPassword, Credential}; @@ -36,7 +33,7 @@ pub struct UnixExtensions { ucred: Option, shell: Option, gidnumber: u32, - groups: Vec, + groups: Vec>, } impl UnixExtensions { @@ -54,7 +51,7 @@ pub struct Account { pub displayname: String, pub uuid: Uuid, pub sync_parent_uuid: Option, - pub groups: Vec, + pub groups: Vec>, pub primary: Option, pub passkeys: BTreeMap, pub attested_passkeys: BTreeMap, @@ -138,7 +135,7 @@ macro_rules! try_from_entry { // Provide hints from groups. let mut ui_hints: BTreeSet<_> = groups .iter() - .map(|group: &Group| group.ui_hints.iter()) + .map(|group: &Group<()>| group.ui_hints().iter()) .flatten() .copied() .collect(); @@ -235,7 +232,7 @@ impl Account { value: &Entry, qs: &mut QueryServerReadTransaction, ) -> Result { - let (groups, unix_groups) = load_all_groups_from_account_entry(value, qs)?; + let (groups, unix_groups) = load_all_groups_from_account(value, qs)?; try_from_entry!(value, groups, unix_groups) } @@ -248,8 +245,8 @@ impl Account { where TXN: QueryServerTransaction<'a>, { - let ((groups, unix_groups), rap) = - load_all_groups_from_account_entry_with_policy(value, qs)?; + let (groups, unix_groups) = load_all_groups_from_account(value, qs)?; + let rap = load_account_policy(value, qs)?; try_from_entry!(value, groups, unix_groups).map(|acct| (acct, rap)) } @@ -259,7 +256,7 @@ impl Account { value: &Entry, qs: &mut QueryServerWriteTransaction, ) -> Result { - let (groups, unix_groups) = load_all_groups_from_account_entry(value, qs)?; + let (groups, unix_groups) = load_all_groups_from_account(value, qs)?; try_from_entry!(value, groups, unix_groups) } @@ -269,7 +266,7 @@ impl Account { value: &Entry, qs: &mut QueryServerReadTransaction, ) -> Result { - let (groups, unix_groups) = load_all_groups_from_account_entry_reduced(value, qs)?; + let (groups, unix_groups) = load_all_groups_from_account(value, qs)?; try_from_entry!(value, groups, unix_groups) } diff --git a/server/lib/src/idm/group.rs b/server/lib/src/idm/group.rs index f89b2f1c5..006264471 100644 --- a/server/lib/src/idm/group.rs +++ b/server/lib/src/idm/group.rs @@ -1,239 +1,72 @@ use std::collections::BTreeSet; -use std::iter; use kanidm_proto::internal::{Group as ProtoGroup, UiHint}; use kanidm_proto::v1::UnixGroupToken; use uuid::Uuid; -use super::accountpolicy::{AccountPolicy, ResolvedAccountPolicy}; -use crate::entry::{Entry, EntryCommitted, EntryReduced, EntrySealed}; +use crate::entry::{Committed, Entry, EntryCommitted, EntrySealed, GetUuid}; use crate::prelude::*; use crate::value::PartialValue; -macro_rules! entry_groups { - ($value:expr, $qs:expr) => {{ - match $value.get_ava_as_refuuid(Attribute::MemberOf) { - Some(riter) => { - // given a list of uuid, make a filter: even if this is empty, the be will - // just give and empty result set. - let f = filter!(f_or( - riter - .map(|u| f_eq(Attribute::Uuid, PartialValue::Uuid(u))) - .collect() - )); - $qs.internal_search(f).map_err(|e| { - admin_error!(?e, "internal search failed"); - e - })? - } - None => { - // No memberof, no groups! - vec![] - } - } - }}; -} +use super::accountpolicy::{AccountPolicy, ResolvedAccountPolicy}; -macro_rules! load_all_groups_from_iter { - ($value:expr, $group_iter:expr) => {{ - let mut groups: Vec = vec![]; - let mut unix_groups: Vec = vec![]; - - let is_unix_account = $value.attribute_equality( - Attribute::Class, - &EntryClass::PosixAccount.to_partialvalue(), - ); - - // Setup the user private group - let spn = $value - .get_ava_single_proto_string(Attribute::Spn) - .ok_or(OperationError::MissingAttribute(Attribute::Spn))?; - - let uuid = $value.get_uuid(); - - // We could allow ui hints on the user direct in the future? - let ui_hints = BTreeSet::default(); - - groups.push(Group { - spn: spn.clone(), - uuid: uuid.clone(), - ui_hints, - }); - - if is_unix_account { - let name = $value - .get_ava_single_proto_string(Attribute::Name) - .ok_or(OperationError::MissingAttribute(Attribute::Name))?; - - let gidnumber = $value - .get_ava_single_uint32(Attribute::GidNumber) - .ok_or(OperationError::MissingAttribute(Attribute::GidNumber))?; - - unix_groups.push(UnixGroup { - name, - spn, - gidnumber, - uuid, - }); - } - - for group_entry in $group_iter { - let group = Group::try_from_entry(group_entry.as_ref())?; - groups.push(group); - - if is_unix_account - && group_entry - .attribute_equality(Attribute::Class, &EntryClass::PosixGroup.to_partialvalue()) - { - let unix_group = UnixGroup::try_from_entry(group_entry.as_ref())?; - unix_groups.push(unix_group); - } - } - - (groups, unix_groups) - }}; -} - -pub(crate) fn load_all_groups_from_account_entry<'a, T>( - value: &Entry, - qs: &mut T, -) -> Result<(Vec, Vec), OperationError> -where - T: QueryServerTransaction<'a>, -{ - let group_iter = entry_groups!(value, qs); - Ok(load_all_groups_from_iter!(value, group_iter)) -} - -pub(crate) fn load_all_groups_from_account_entry_with_policy<'a, T>( - value: &Entry, - qs: &mut T, -) -> Result<((Vec, Vec), ResolvedAccountPolicy), OperationError> -where - T: QueryServerTransaction<'a>, -{ - let group_iter = entry_groups!(value, qs); - - let rap = ResolvedAccountPolicy::fold_from(group_iter.iter().filter_map(|entry| { - let acc_pol: Option = entry.as_ref().into(); - acc_pol - })); - - Ok((load_all_groups_from_iter!(value, group_iter), rap)) -} - -pub(crate) fn load_all_groups_from_account_entry_reduced<'a, T>( - value: &Entry, - qs: &mut T, -) -> Result<(Vec, Vec), OperationError> -where - T: QueryServerTransaction<'a>, -{ - let group_iter = entry_groups!(value, qs); - Ok(load_all_groups_from_iter!(value, group_iter)) -} +// I hate that rust is forcing this to be public +pub trait GroupType {} #[derive(Debug, Clone)] -pub struct Group { +pub(crate) struct Unix { + name: String, + gidnumber: u32, +} + +impl GroupType for Unix {} + +impl GroupType for () {} + +#[derive(Debug, Clone)] +pub struct Group +where + T: GroupType, +{ + inner: T, spn: String, uuid: Uuid, // We'll probably add policy and claims later to this - pub ui_hints: BTreeSet, + ui_hints: BTreeSet, } -macro_rules! upg_from_account_e { - ($value:expr, $groups:expr) => {{ - // Setup the user private group +macro_rules! try_from_entry { + ($value:expr, $inner:expr) => {{ let spn = $value - .get_ava_single_proto_string(Attribute::Spn) - .ok_or(OperationError::MissingAttribute(Attribute::Spn))?; - - let uuid = $value.get_uuid(); - - // We could allow ui hints on the user direct in the future? - let ui_hints = BTreeSet::default(); - - let upg = Group { - spn, - uuid, - ui_hints, - }; - - // Now convert the group entries to groups. - let groups: Result, _> = $groups - .iter() - .map(|e| Group::try_from_entry(e.as_ref())) - .chain(std::iter::once(Ok(upg))) - .collect(); - - groups.map_err(|e| { - error!(?e, "failed to transform group entries to groups"); - e - }) - }}; -} - -impl Group { - pub(crate) fn uuid(&self) -> Uuid { - self.uuid - } - - pub fn try_from_account_entry_reduced<'a, TXN>( - value: &Entry, - qs: &mut TXN, - ) -> Result, OperationError> - where - TXN: QueryServerTransaction<'a>, - { - let groups = entry_groups!(value, qs); - upg_from_account_e!(value, groups) - } - - pub fn try_from_account_entry<'a, TXN>( - value: &Entry, - qs: &mut TXN, - ) -> Result, OperationError> - where - TXN: QueryServerTransaction<'a>, - { - let groups = entry_groups!(value, qs); - upg_from_account_e!(value, groups) - } - - pub fn try_from_entry( - value: &Entry, - ) -> Result { - if !value.attribute_equality(Attribute::Class, &EntryClass::Group.into()) { - return Err(OperationError::MissingAttribute(Attribute::Group)); - } - - // Now extract our needed attributes - /* - let name = value - .get_ava_single_iname(Attribute::Name) - .map(|s| s.to_string()) - .ok_or_else(|| { - OperationError::MissingAttribute(Attribute::Name) - })?; - */ - let spn = value .get_ava_single_proto_string(Attribute::Spn) .ok_or_else(|| OperationError::MissingAttribute(Attribute::Spn))?; - let uuid = value.get_uuid(); + let uuid = $value.get_uuid(); - let ui_hints = value + let ui_hints = $value .get_ava_uihint(Attribute::GrantUiHint) .cloned() .unwrap_or_default(); - Ok(Group { + Ok(Self { + inner: $inner, spn, uuid, ui_hints, }) - } + }}; +} +impl Group { + pub fn spn(&self) -> &String { + &self.spn + } + pub fn uuid(&self) -> &Uuid { + &self.uuid + } + pub fn ui_hints(&self) -> &BTreeSet { + &self.ui_hints + } pub fn to_proto(&self) -> ProtoGroup { ProtoGroup { spn: self.spn.clone(), @@ -242,160 +75,248 @@ impl Group { } } -#[derive(Debug, Clone)] -pub(crate) struct UnixGroup { - pub name: String, - pub spn: String, - pub gidnumber: u32, - pub uuid: Uuid, -} +macro_rules! try_from_account { + ($value:expr, $qs:expr) => {{ + let Some(iter) = $value.get_ava_as_refuuid(Attribute::MemberOf) else { + return Ok(vec![]); + }; -macro_rules! try_from_group_e { - ($value:expr) => {{ - // We could be looking at a user for their UPG, OR a true group. + // given a list of uuid, make a filter: even if this is empty, the be will + // just give and empty result set. + let f = filter!(f_or( + iter.map(|u| f_eq(Attribute::Uuid, PartialValue::Uuid(u))) + .collect() + )); - if !(($value.attribute_equality(Attribute::Class, &EntryClass::Account.to_partialvalue()) - && $value.attribute_equality( - Attribute::Class, - &EntryClass::PosixAccount.to_partialvalue(), - )) - || ($value.attribute_equality(Attribute::Class, &EntryClass::Group.to_partialvalue()) - && $value.attribute_equality( - Attribute::Class, - &EntryClass::PosixGroup.to_partialvalue(), - ))) - { - return Err(OperationError::InvalidAccountState(format!( - "Missing {}: {} && {} OR {} && {}", - Attribute::Class, - Attribute::Account, - EntryClass::PosixAccount, - Attribute::Group, - EntryClass::PosixGroup, - ))); - } + let entries = $qs.internal_search(f).map_err(|e| { + admin_error!(?e, "internal search failed"); + e + })?; - let name = $value - .get_ava_single_iname(Attribute::Name) - .map(|s| s.to_string()) - .ok_or_else(|| OperationError::MissingAttribute(Attribute::Name))?; - - let spn = $value - .get_ava_single_proto_string(Attribute::Spn) - .ok_or_else(|| OperationError::MissingAttribute(Attribute::Spn))?; - - let uuid = $value.get_uuid(); - - let gidnumber = $value - .get_ava_single_uint32(Attribute::GidNumber) - .ok_or_else(|| OperationError::MissingAttribute(Attribute::GidNumber))?; - - Ok(UnixGroup { - name, - spn, - gidnumber, - uuid, - }) + Ok(entries + .iter() + .map(|entry| Self::try_from_entry(&entry)) + .filter_map(|v| v.ok()) + .collect()) }}; } -macro_rules! try_from_account_group_e { - ($value:expr, $qs:expr) => {{ - // First synthesise the self-group from the account. - // We have already checked these, but paranoia is better than - // complacency. - if !$value.attribute_equality(Attribute::Class, &EntryClass::Account.to_partialvalue()) { +impl Group<()> { + pub fn try_from_account<'a, TXN>( + value: &Entry, + qs: &mut TXN, + ) -> Result>, OperationError> + where + TXN: QueryServerTransaction<'a>, + { + if !value.attribute_equality(Attribute::Class, &EntryClass::Account.into()) { return Err(OperationError::MissingClass(ENTRYCLASS_ACCOUNT.into())); } - if !$value.attribute_equality( - Attribute::Class, - &EntryClass::PosixAccount.to_partialvalue(), - ) { + let user_group = try_from_entry!(value, ())?; + Ok(std::iter::once(user_group) + .chain(Self::try_from_account_reduced(value, qs)?) + .collect()) + } + + pub fn try_from_account_reduced<'a, E, TXN>( + value: &Entry, + qs: &mut TXN, + ) -> Result>, OperationError> + where + E: Committed, + TXN: QueryServerTransaction<'a>, + { + try_from_account!(value, qs) + } + + pub fn try_from_entry(value: &Entry) -> Result + where + E: Committed, + Entry: GetUuid, + { + if !value.attribute_equality(Attribute::Class, &EntryClass::Group.into()) { + return Err(OperationError::MissingAttribute(Attribute::Group)); + } + + try_from_entry!(value, ()) + } +} + +impl Group { + pub fn try_from_account<'a, TXN>( + value: &Entry, + qs: &mut TXN, + ) -> Result>, OperationError> + where + TXN: QueryServerTransaction<'a>, + { + if !value.attribute_equality(Attribute::Class, &EntryClass::Account.into()) { + return Err(OperationError::MissingClass(ENTRYCLASS_ACCOUNT.into())); + } + + if !value.attribute_equality(Attribute::Class, &EntryClass::PosixAccount.into()) { return Err(OperationError::MissingClass( ENTRYCLASS_POSIX_ACCOUNT.into(), )); } - let name = $value + let name = value .get_ava_single_iname(Attribute::Name) .map(|s| s.to_string()) .ok_or_else(|| OperationError::MissingAttribute(Attribute::Name))?; - let spn = $value - .get_ava_single_proto_string(Attribute::Spn) - .ok_or_else(|| OperationError::MissingAttribute(Attribute::Spn))?; - - let uuid = $value.get_uuid(); - - let gidnumber = $value + let gidnumber = value .get_ava_single_uint32(Attribute::GidNumber) .ok_or_else(|| OperationError::MissingAttribute(Attribute::GidNumber))?; - // This is the user private group. - let upg = UnixGroup { - name, - spn, - gidnumber, - uuid, - }; + let user_group = try_from_entry!(value, Unix { name, gidnumber })?; - match $value.get_ava_as_refuuid(Attribute::MemberOf) { - Some(riter) => { - let f = filter!(f_and!([ - f_eq(Attribute::Class, EntryClass::PosixGroup.into()), - f_eq(Attribute::Class, EntryClass::Group.into()), - f_or( - riter - .map(|u| f_eq(Attribute::Uuid, PartialValue::Uuid(u))) - .collect() - ) - ])); - let group_entries: Vec<_> = $qs.internal_search(f)?; - let groups: Result, _> = iter::once(Ok(upg)) - .chain( - group_entries - .iter() - .map(|e| UnixGroup::try_from_entry(e.as_ref())), - ) - .collect(); - groups + Ok(std::iter::once(user_group) + .chain(Self::try_from_account_reduced(value, qs)?) + .collect()) + } + + pub fn try_from_account_reduced<'a, E, TXN>( + value: &Entry, + qs: &mut TXN, + ) -> Result>, OperationError> + where + E: Committed, + TXN: QueryServerTransaction<'a>, + { + try_from_account!(value, qs) + } + + fn check_entry_classes(value: &Entry) -> Result<(), OperationError> + where + E: Committed, + Entry: GetUuid, + { + // If its an account, it must be a posix account + if value.attribute_equality(Attribute::Class, &EntryClass::Account.into()) { + if !value.attribute_equality(Attribute::Class, &EntryClass::PosixAccount.into()) { + return Err(OperationError::MissingClass( + ENTRYCLASS_POSIX_ACCOUNT.into(), + )); } - None => { - // No memberof, no groups! - Ok(vec![upg]) + } else { + // Otherwise it must be both a group and a posix group + if !value.attribute_equality(Attribute::Class, &EntryClass::PosixGroup.into()) { + return Err(OperationError::MissingClass(ENTRYCLASS_POSIX_GROUP.into())); + } + + if !value.attribute_equality(Attribute::Class, &EntryClass::Group.into()) { + return Err(OperationError::MissingAttribute(Attribute::Group)); } } - }}; -} - -impl UnixGroup { - #[allow(dead_code)] - pub(crate) fn try_from_account_entry_rw( - value: &Entry, - qs: &mut QueryServerWriteTransaction, - ) -> Result, OperationError> { - try_from_account_group_e!(value, qs) + Ok(()) } - pub(crate) fn try_from_entry_reduced( - value: &Entry, - ) -> Result { - try_from_group_e!(value) - } + pub fn try_from_entry(value: &Entry) -> Result + where + E: Committed, + Entry: GetUuid, + { + Self::check_entry_classes(value)?; - pub(crate) fn try_from_entry( - value: &Entry, - ) -> Result { - try_from_group_e!(value) + let name = value + .get_ava_single_iname(Attribute::Name) + .map(|s| s.to_string()) + .ok_or_else(|| OperationError::MissingAttribute(Attribute::Name))?; + + let gidnumber = value + .get_ava_single_uint32(Attribute::GidNumber) + .ok_or_else(|| OperationError::MissingAttribute(Attribute::GidNumber))?; + + try_from_entry!(value, Unix { name, gidnumber }) } pub(crate) fn to_unixgrouptoken(&self) -> UnixGroupToken { UnixGroupToken { - name: self.name.clone(), + name: self.inner.name.clone(), spn: self.spn.clone(), uuid: self.uuid, - gidnumber: self.gidnumber, + gidnumber: self.inner.gidnumber, } } } + +pub(crate) fn load_account_policy<'a, T>( + value: &Entry, + qs: &mut T, +) -> Result +where + T: QueryServerTransaction<'a>, +{ + let iter = match value.get_ava_as_refuuid(Attribute::MemberOf) { + Some(v) => v, + None => Box::new(Vec::::new().into_iter()), + }; + + // given a list of uuid, make a filter: even if this is empty, the be will + // just give and empty result set. + let f = filter!(f_or( + iter.map(|u| f_eq(Attribute::Uuid, PartialValue::Uuid(u))) + .collect() + )); + + let entries = qs.internal_search(f).map_err(|e| { + admin_error!(?e, "internal search failed"); + e + })?; + + Ok(ResolvedAccountPolicy::fold_from(entries.iter().filter_map( + |entry| { + let acc_pol: Option = entry.as_ref().into(); + acc_pol + }, + ))) +} + +pub(crate) fn load_all_groups_from_account<'a, E, TXN>( + value: &Entry, + qs: &mut TXN, +) -> Result<(Vec>, Vec>), OperationError> +where + E: Committed, + Entry: GetUuid, + TXN: QueryServerTransaction<'a>, +{ + let Some(iter) = value.get_ava_as_refuuid(Attribute::MemberOf) else { + return Ok((vec![], vec![])); + }; + + let conditions: Vec<_> = iter + .map(|u| f_eq(Attribute::Uuid, PartialValue::Uuid(u))) + .collect(); + + println!("{:?}", conditions); + + let f = filter!(f_or(conditions)); + + let entries = qs.internal_search(f).map_err(|e| { + admin_error!(?e, "internal search failed"); + e + })?; + + println!("{}", entries.len()); + + let mut groups = vec![]; + let mut unix_groups = Group::::try_from_entry(value) + .ok() + .into_iter() + .collect::>(); + + for entry in entries.iter() { + let entry = entry.as_ref(); + if entry.attribute_equality(Attribute::Class, &EntryClass::PosixGroup.into()) { + unix_groups.push(Group::::try_from_entry::(entry)?); + } + + // No idea why we need to explicitly specify the type here + groups.push(Group::<()>::try_from_entry::(entry)?); + } + + Ok((groups, unix_groups)) +} diff --git a/server/lib/src/idm/oauth2.rs b/server/lib/src/idm/oauth2.rs index c65619b10..a27503e68 100644 --- a/server/lib/src/idm/oauth2.rs +++ b/server/lib/src/idm/oauth2.rs @@ -2635,7 +2635,7 @@ fn extra_claims_for_account( // for each group for group_uuid in account.groups.iter().map(|g| g.uuid()) { // Does this group have any custom claims? - if let Some(claim) = claim_map.get(&group_uuid) { + if let Some(claim) = claim_map.get(group_uuid) { // If so, iterate over the set of claims and values. for (claim_name, claim_value) in claim.iter() { // Does this claim name already exist in our in-progress map? diff --git a/server/lib/src/idm/radius.rs b/server/lib/src/idm/radius.rs index 37c7f6d53..3b7aba116 100644 --- a/server/lib/src/idm/radius.rs +++ b/server/lib/src/idm/radius.rs @@ -13,7 +13,7 @@ pub(crate) struct RadiusAccount { pub name: String, pub displayname: String, pub uuid: Uuid, - pub groups: Vec, + pub groups: Vec>, pub radius_secret: String, pub valid_from: Option, pub expire: Option, @@ -45,7 +45,7 @@ impl RadiusAccount { .map(|s| s.to_string()) .ok_or_else(|| OperationError::MissingAttribute(Attribute::DisplayName))?; - let groups = Group::try_from_account_entry_reduced(value, qs)?; + let groups = Group::<()>::try_from_account_reduced(value, qs)?; let valid_from = value.get_ava_single_datetime(Attribute::AccountValidFrom); diff --git a/server/lib/src/idm/server.rs b/server/lib/src/idm/server.rs index 68cb93d01..2191eba2b 100644 --- a/server/lib/src/idm/server.rs +++ b/server/lib/src/idm/server.rs @@ -46,7 +46,7 @@ use crate::idm::event::{ RegenerateRadiusSecretEvent, UnixGroupTokenEvent, UnixPasswordChangeEvent, UnixUserAuthEvent, UnixUserTokenEvent, }; -use crate::idm::group::UnixGroup; +use crate::idm::group::{Group, Unix}; use crate::idm::oauth2::{ Oauth2ResourceServers, Oauth2ResourceServersReadTransaction, Oauth2ResourceServersWriteTransaction, @@ -1575,7 +1575,7 @@ impl<'a> IdmServerProxyReadTransaction<'a> { let group = self .qs_read .impersonate_search_ext_uuid(uute.target, &uute.ident) - .and_then(|e| UnixGroup::try_from_entry_reduced(&e)) + .and_then(|e| Group::::try_from_entry(&e)) .map_err(|e| { admin_error!("Failed to start unix group token {:?}", e); e diff --git a/server/testkit/tests/group.rs b/server/testkit/tests/group.rs index 971fa081b..624eecb98 100644 --- a/server/testkit/tests/group.rs +++ b/server/testkit/tests/group.rs @@ -1,5 +1,6 @@ use kanidm_client::{ClientError, KanidmClient}; use kanidm_proto::constants::ATTR_DESCRIPTION; +use kanidmd_lib::idm::group::Group; use kanidmd_testkit::{create_user, ADMIN_TEST_PASSWORD, ADMIN_TEST_USER}; use serde_json::Value;