Chore: Refactor Groups to be more generic (#3136)

This commit is contained in:
CEbbinghaus 2024-10-25 11:36:20 +11:00 committed by GitHub
parent d2ae2ca206
commit dc56a3217d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 286 additions and 343 deletions

View file

@ -166,6 +166,15 @@ pub struct EntryReduced {
// Today is that day - @firstyear // Today is that day - @firstyear
pub type Eattrs = Map<Attribute, ValueSet>; pub type Eattrs = Map<Attribute, ValueSet>;
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 { pub(crate) fn compare_attrs(left: &Eattrs, right: &Eattrs) -> bool {
// We can't shortcut based on len because cid mod may not be present. // We can't shortcut based on len because cid mod may not be present.
// Build the set of all keys between both. // Build the set of all keys between both.
@ -260,7 +269,7 @@ where
STATE: Clone, STATE: Clone,
{ {
/// Get the uuid of this entry. /// Get the uuid of this entry.
pub(crate) fn get_uuid(&self) -> Option<Uuid> { pub fn get_uuid(&self) -> Option<Uuid> {
self.attrs self.attrs
.get(&Attribute::Uuid) .get(&Attribute::Uuid)
.and_then(|vs| vs.to_uuid_single()) .and_then(|vs| vs.to_uuid_single())
@ -2127,6 +2136,15 @@ impl<STATE> Entry<EntryValid, STATE> {
} }
} }
impl<STATE> GetUuid for Entry<EntrySealed, STATE>
where
STATE: Clone,
{
fn get_uuid(&self) -> Uuid {
self.valid.uuid
}
}
impl<STATE> Entry<EntrySealed, STATE> impl<STATE> Entry<EntrySealed, STATE>
where where
STATE: Clone, STATE: Clone,
@ -2213,6 +2231,12 @@ where
} }
} }
impl GetUuid for Entry<EntryReduced, EntryCommitted> {
fn get_uuid(&self) -> Uuid {
self.valid.uuid
}
}
impl Entry<EntryReduced, EntryCommitted> { impl Entry<EntryReduced, EntryCommitted> {
pub fn get_uuid(&self) -> Uuid { pub fn get_uuid(&self) -> Uuid {
self.valid.uuid self.valid.uuid

View file

@ -12,10 +12,7 @@ use webauthn_rs::prelude::{
}; };
use super::accountpolicy::ResolvedAccountPolicy; use super::accountpolicy::ResolvedAccountPolicy;
use super::group::{ use super::group::{load_account_policy, load_all_groups_from_account, Group, Unix};
load_all_groups_from_account_entry, load_all_groups_from_account_entry_reduced,
load_all_groups_from_account_entry_with_policy, Group, UnixGroup,
};
use crate::constants::UUID_ANONYMOUS; use crate::constants::UUID_ANONYMOUS;
use crate::credential::softlock::CredSoftLockPolicy; use crate::credential::softlock::CredSoftLockPolicy;
use crate::credential::{apppwd::ApplicationPassword, Credential}; use crate::credential::{apppwd::ApplicationPassword, Credential};
@ -36,7 +33,7 @@ pub struct UnixExtensions {
ucred: Option<Credential>, ucred: Option<Credential>,
shell: Option<String>, shell: Option<String>,
gidnumber: u32, gidnumber: u32,
groups: Vec<UnixGroup>, groups: Vec<Group<Unix>>,
} }
impl UnixExtensions { impl UnixExtensions {
@ -54,7 +51,7 @@ pub struct Account {
pub displayname: String, pub displayname: String,
pub uuid: Uuid, pub uuid: Uuid,
pub sync_parent_uuid: Option<Uuid>, pub sync_parent_uuid: Option<Uuid>,
pub groups: Vec<Group>, pub groups: Vec<Group<()>>,
pub primary: Option<Credential>, pub primary: Option<Credential>,
pub passkeys: BTreeMap<Uuid, (String, PasskeyV4)>, pub passkeys: BTreeMap<Uuid, (String, PasskeyV4)>,
pub attested_passkeys: BTreeMap<Uuid, (String, AttestedPasskeyV4)>, pub attested_passkeys: BTreeMap<Uuid, (String, AttestedPasskeyV4)>,
@ -138,7 +135,7 @@ macro_rules! try_from_entry {
// Provide hints from groups. // Provide hints from groups.
let mut ui_hints: BTreeSet<_> = groups let mut ui_hints: BTreeSet<_> = groups
.iter() .iter()
.map(|group: &Group| group.ui_hints.iter()) .map(|group: &Group<()>| group.ui_hints().iter())
.flatten() .flatten()
.copied() .copied()
.collect(); .collect();
@ -235,7 +232,7 @@ impl Account {
value: &Entry<EntrySealed, EntryCommitted>, value: &Entry<EntrySealed, EntryCommitted>,
qs: &mut QueryServerReadTransaction, qs: &mut QueryServerReadTransaction,
) -> Result<Self, OperationError> { ) -> Result<Self, OperationError> {
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) try_from_entry!(value, groups, unix_groups)
} }
@ -248,8 +245,8 @@ impl Account {
where where
TXN: QueryServerTransaction<'a>, TXN: QueryServerTransaction<'a>,
{ {
let ((groups, unix_groups), rap) = let (groups, unix_groups) = load_all_groups_from_account(value, qs)?;
load_all_groups_from_account_entry_with_policy(value, qs)?; let rap = load_account_policy(value, qs)?;
try_from_entry!(value, groups, unix_groups).map(|acct| (acct, rap)) try_from_entry!(value, groups, unix_groups).map(|acct| (acct, rap))
} }
@ -259,7 +256,7 @@ impl Account {
value: &Entry<EntrySealed, EntryCommitted>, value: &Entry<EntrySealed, EntryCommitted>,
qs: &mut QueryServerWriteTransaction, qs: &mut QueryServerWriteTransaction,
) -> Result<Self, OperationError> { ) -> Result<Self, OperationError> {
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) try_from_entry!(value, groups, unix_groups)
} }
@ -269,7 +266,7 @@ impl Account {
value: &Entry<EntryReduced, EntryCommitted>, value: &Entry<EntryReduced, EntryCommitted>,
qs: &mut QueryServerReadTransaction, qs: &mut QueryServerReadTransaction,
) -> Result<Self, OperationError> { ) -> Result<Self, OperationError> {
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) try_from_entry!(value, groups, unix_groups)
} }

View file

@ -1,239 +1,72 @@
use std::collections::BTreeSet; use std::collections::BTreeSet;
use std::iter;
use kanidm_proto::internal::{Group as ProtoGroup, UiHint}; use kanidm_proto::internal::{Group as ProtoGroup, UiHint};
use kanidm_proto::v1::UnixGroupToken; use kanidm_proto::v1::UnixGroupToken;
use uuid::Uuid; use uuid::Uuid;
use super::accountpolicy::{AccountPolicy, ResolvedAccountPolicy}; use crate::entry::{Committed, Entry, EntryCommitted, EntrySealed, GetUuid};
use crate::entry::{Entry, EntryCommitted, EntryReduced, EntrySealed};
use crate::prelude::*; use crate::prelude::*;
use crate::value::PartialValue; use crate::value::PartialValue;
macro_rules! entry_groups { use super::accountpolicy::{AccountPolicy, ResolvedAccountPolicy};
($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![]
}
}
}};
}
macro_rules! load_all_groups_from_iter { // I hate that rust is forcing this to be public
($value:expr, $group_iter:expr) => {{ pub trait GroupType {}
let mut groups: Vec<Group> = vec![];
let mut unix_groups: Vec<UnixGroup> = 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<EntrySealed, EntryCommitted>,
qs: &mut T,
) -> Result<(Vec<Group>, Vec<UnixGroup>), 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<EntrySealed, EntryCommitted>,
qs: &mut T,
) -> Result<((Vec<Group>, Vec<UnixGroup>), 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<AccountPolicy> = 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<EntryReduced, EntryCommitted>,
qs: &mut T,
) -> Result<(Vec<Group>, Vec<UnixGroup>), OperationError>
where
T: QueryServerTransaction<'a>,
{
let group_iter = entry_groups!(value, qs);
Ok(load_all_groups_from_iter!(value, group_iter))
}
#[derive(Debug, Clone)] #[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<T>
where
T: GroupType,
{
inner: T,
spn: String, spn: String,
uuid: Uuid, uuid: Uuid,
// We'll probably add policy and claims later to this // We'll probably add policy and claims later to this
pub ui_hints: BTreeSet<UiHint>, ui_hints: BTreeSet<UiHint>,
} }
macro_rules! upg_from_account_e { macro_rules! try_from_entry {
($value:expr, $groups:expr) => {{ ($value:expr, $inner:expr) => {{
// Setup the user private group
let spn = $value 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<Vec<_>, _> = $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<EntryReduced, EntryCommitted>,
qs: &mut TXN,
) -> Result<Vec<Self>, 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<EntrySealed, EntryCommitted>,
qs: &mut TXN,
) -> Result<Vec<Self>, OperationError>
where
TXN: QueryServerTransaction<'a>,
{
let groups = entry_groups!(value, qs);
upg_from_account_e!(value, groups)
}
pub fn try_from_entry(
value: &Entry<EntrySealed, EntryCommitted>,
) -> Result<Self, OperationError> {
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) .get_ava_single_proto_string(Attribute::Spn)
.ok_or_else(|| OperationError::MissingAttribute(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) .get_ava_uihint(Attribute::GrantUiHint)
.cloned() .cloned()
.unwrap_or_default(); .unwrap_or_default();
Ok(Group { Ok(Self {
inner: $inner,
spn, spn,
uuid, uuid,
ui_hints, ui_hints,
}) })
} }};
}
impl<T: GroupType> Group<T> {
pub fn spn(&self) -> &String {
&self.spn
}
pub fn uuid(&self) -> &Uuid {
&self.uuid
}
pub fn ui_hints(&self) -> &BTreeSet<UiHint> {
&self.ui_hints
}
pub fn to_proto(&self) -> ProtoGroup { pub fn to_proto(&self) -> ProtoGroup {
ProtoGroup { ProtoGroup {
spn: self.spn.clone(), spn: self.spn.clone(),
@ -242,160 +75,248 @@ impl Group {
} }
} }
#[derive(Debug, Clone)] macro_rules! try_from_account {
pub(crate) struct UnixGroup { ($value:expr, $qs:expr) => {{
pub name: String, let Some(iter) = $value.get_ava_as_refuuid(Attribute::MemberOf) else {
pub spn: String, return Ok(vec![]);
pub gidnumber: u32, };
pub uuid: Uuid,
}
macro_rules! try_from_group_e { // given a list of uuid, make a filter: even if this is empty, the be will
($value:expr) => {{ // just give and empty result set.
// We could be looking at a user for their UPG, OR a true group. 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()) let entries = $qs.internal_search(f).map_err(|e| {
&& $value.attribute_equality( admin_error!(?e, "internal search failed");
Attribute::Class, e
&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 name = $value Ok(entries
.get_ava_single_iname(Attribute::Name) .iter()
.map(|s| s.to_string()) .map(|entry| Self::try_from_entry(&entry))
.ok_or_else(|| OperationError::MissingAttribute(Attribute::Name))?; .filter_map(|v| v.ok())
.collect())
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,
})
}}; }};
} }
macro_rules! try_from_account_group_e { impl Group<()> {
($value:expr, $qs:expr) => {{ pub fn try_from_account<'a, TXN>(
// First synthesise the self-group from the account. value: &Entry<EntrySealed, EntryCommitted>,
// We have already checked these, but paranoia is better than qs: &mut TXN,
// complacency. ) -> Result<Vec<Group<()>>, OperationError>
if !$value.attribute_equality(Attribute::Class, &EntryClass::Account.to_partialvalue()) { where
TXN: QueryServerTransaction<'a>,
{
if !value.attribute_equality(Attribute::Class, &EntryClass::Account.into()) {
return Err(OperationError::MissingClass(ENTRYCLASS_ACCOUNT.into())); return Err(OperationError::MissingClass(ENTRYCLASS_ACCOUNT.into()));
} }
if !$value.attribute_equality( let user_group = try_from_entry!(value, ())?;
Attribute::Class, Ok(std::iter::once(user_group)
&EntryClass::PosixAccount.to_partialvalue(), .chain(Self::try_from_account_reduced(value, qs)?)
) { .collect())
}
pub fn try_from_account_reduced<'a, E, TXN>(
value: &Entry<E, EntryCommitted>,
qs: &mut TXN,
) -> Result<Vec<Group<()>>, OperationError>
where
E: Committed,
TXN: QueryServerTransaction<'a>,
{
try_from_account!(value, qs)
}
pub fn try_from_entry<E>(value: &Entry<E, EntryCommitted>) -> Result<Self, OperationError>
where
E: Committed,
Entry<E, EntryCommitted>: GetUuid,
{
if !value.attribute_equality(Attribute::Class, &EntryClass::Group.into()) {
return Err(OperationError::MissingAttribute(Attribute::Group));
}
try_from_entry!(value, ())
}
}
impl Group<Unix> {
pub fn try_from_account<'a, TXN>(
value: &Entry<EntrySealed, EntryCommitted>,
qs: &mut TXN,
) -> Result<Vec<Group<Unix>>, 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( return Err(OperationError::MissingClass(
ENTRYCLASS_POSIX_ACCOUNT.into(), ENTRYCLASS_POSIX_ACCOUNT.into(),
)); ));
} }
let name = $value let name = value
.get_ava_single_iname(Attribute::Name) .get_ava_single_iname(Attribute::Name)
.map(|s| s.to_string()) .map(|s| s.to_string())
.ok_or_else(|| OperationError::MissingAttribute(Attribute::Name))?; .ok_or_else(|| OperationError::MissingAttribute(Attribute::Name))?;
let spn = $value let gidnumber = 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) .get_ava_single_uint32(Attribute::GidNumber)
.ok_or_else(|| OperationError::MissingAttribute(Attribute::GidNumber))?; .ok_or_else(|| OperationError::MissingAttribute(Attribute::GidNumber))?;
// This is the user private group. let user_group = try_from_entry!(value, Unix { name, gidnumber })?;
let upg = UnixGroup {
name,
spn,
gidnumber,
uuid,
};
match $value.get_ava_as_refuuid(Attribute::MemberOf) { Ok(std::iter::once(user_group)
Some(riter) => { .chain(Self::try_from_account_reduced(value, qs)?)
let f = filter!(f_and!([ .collect())
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<Vec<_>, _> = iter::once(Ok(upg))
.chain(
group_entries
.iter()
.map(|e| UnixGroup::try_from_entry(e.as_ref())),
)
.collect();
groups
}
None => {
// No memberof, no groups!
Ok(vec![upg])
}
}
}};
}
impl UnixGroup {
#[allow(dead_code)]
pub(crate) fn try_from_account_entry_rw(
value: &Entry<EntrySealed, EntryCommitted>,
qs: &mut QueryServerWriteTransaction,
) -> Result<Vec<Self>, OperationError> {
try_from_account_group_e!(value, qs)
} }
pub(crate) fn try_from_entry_reduced( pub fn try_from_account_reduced<'a, E, TXN>(
value: &Entry<EntryReduced, EntryCommitted>, value: &Entry<E, EntryCommitted>,
) -> Result<Self, OperationError> { qs: &mut TXN,
try_from_group_e!(value) ) -> Result<Vec<Group<Unix>>, OperationError>
where
E: Committed,
TXN: QueryServerTransaction<'a>,
{
try_from_account!(value, qs)
} }
pub(crate) fn try_from_entry( fn check_entry_classes<E>(value: &Entry<E, EntryCommitted>) -> Result<(), OperationError>
value: &Entry<EntrySealed, EntryCommitted>, where
) -> Result<Self, OperationError> { E: Committed,
try_from_group_e!(value) Entry<E, EntryCommitted>: 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(),
));
}
} 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));
}
}
Ok(())
}
pub fn try_from_entry<E>(value: &Entry<E, EntryCommitted>) -> Result<Self, OperationError>
where
E: Committed,
Entry<E, EntryCommitted>: GetUuid,
{
Self::check_entry_classes(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 { pub(crate) fn to_unixgrouptoken(&self) -> UnixGroupToken {
UnixGroupToken { UnixGroupToken {
name: self.name.clone(), name: self.inner.name.clone(),
spn: self.spn.clone(), spn: self.spn.clone(),
uuid: self.uuid, uuid: self.uuid,
gidnumber: self.gidnumber, gidnumber: self.inner.gidnumber,
} }
} }
} }
pub(crate) fn load_account_policy<'a, T>(
value: &Entry<EntrySealed, EntryCommitted>,
qs: &mut T,
) -> Result<ResolvedAccountPolicy, OperationError>
where
T: QueryServerTransaction<'a>,
{
let iter = match value.get_ava_as_refuuid(Attribute::MemberOf) {
Some(v) => v,
None => Box::new(Vec::<Uuid>::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<AccountPolicy> = entry.as_ref().into();
acc_pol
},
)))
}
pub(crate) fn load_all_groups_from_account<'a, E, TXN>(
value: &Entry<E, EntryCommitted>,
qs: &mut TXN,
) -> Result<(Vec<Group<()>>, Vec<Group<Unix>>), OperationError>
where
E: Committed,
Entry<E, EntryCommitted>: 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::<Unix>::try_from_entry(value)
.ok()
.into_iter()
.collect::<Vec<_>>();
for entry in entries.iter() {
let entry = entry.as_ref();
if entry.attribute_equality(Attribute::Class, &EntryClass::PosixGroup.into()) {
unix_groups.push(Group::<Unix>::try_from_entry::<EntrySealed>(entry)?);
}
// No idea why we need to explicitly specify the type here
groups.push(Group::<()>::try_from_entry::<EntrySealed>(entry)?);
}
Ok((groups, unix_groups))
}

View file

@ -2635,7 +2635,7 @@ fn extra_claims_for_account(
// for each group // for each group
for group_uuid in account.groups.iter().map(|g| g.uuid()) { for group_uuid in account.groups.iter().map(|g| g.uuid()) {
// Does this group have any custom claims? // 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. // If so, iterate over the set of claims and values.
for (claim_name, claim_value) in claim.iter() { for (claim_name, claim_value) in claim.iter() {
// Does this claim name already exist in our in-progress map? // Does this claim name already exist in our in-progress map?

View file

@ -13,7 +13,7 @@ pub(crate) struct RadiusAccount {
pub name: String, pub name: String,
pub displayname: String, pub displayname: String,
pub uuid: Uuid, pub uuid: Uuid,
pub groups: Vec<Group>, pub groups: Vec<Group<()>>,
pub radius_secret: String, pub radius_secret: String,
pub valid_from: Option<OffsetDateTime>, pub valid_from: Option<OffsetDateTime>,
pub expire: Option<OffsetDateTime>, pub expire: Option<OffsetDateTime>,
@ -45,7 +45,7 @@ impl RadiusAccount {
.map(|s| s.to_string()) .map(|s| s.to_string())
.ok_or_else(|| OperationError::MissingAttribute(Attribute::DisplayName))?; .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); let valid_from = value.get_ava_single_datetime(Attribute::AccountValidFrom);

View file

@ -46,7 +46,7 @@ use crate::idm::event::{
RegenerateRadiusSecretEvent, UnixGroupTokenEvent, UnixPasswordChangeEvent, UnixUserAuthEvent, RegenerateRadiusSecretEvent, UnixGroupTokenEvent, UnixPasswordChangeEvent, UnixUserAuthEvent,
UnixUserTokenEvent, UnixUserTokenEvent,
}; };
use crate::idm::group::UnixGroup; use crate::idm::group::{Group, Unix};
use crate::idm::oauth2::{ use crate::idm::oauth2::{
Oauth2ResourceServers, Oauth2ResourceServersReadTransaction, Oauth2ResourceServers, Oauth2ResourceServersReadTransaction,
Oauth2ResourceServersWriteTransaction, Oauth2ResourceServersWriteTransaction,
@ -1575,7 +1575,7 @@ impl<'a> IdmServerProxyReadTransaction<'a> {
let group = self let group = self
.qs_read .qs_read
.impersonate_search_ext_uuid(uute.target, &uute.ident) .impersonate_search_ext_uuid(uute.target, &uute.ident)
.and_then(|e| UnixGroup::try_from_entry_reduced(&e)) .and_then(|e| Group::<Unix>::try_from_entry(&e))
.map_err(|e| { .map_err(|e| {
admin_error!("Failed to start unix group token {:?}", e); admin_error!("Failed to start unix group token {:?}", e);
e e

View file

@ -1,5 +1,6 @@
use kanidm_client::{ClientError, KanidmClient}; use kanidm_client::{ClientError, KanidmClient};
use kanidm_proto::constants::ATTR_DESCRIPTION; use kanidm_proto::constants::ATTR_DESCRIPTION;
use kanidmd_lib::idm::group::Group;
use kanidmd_testkit::{create_user, ADMIN_TEST_PASSWORD, ADMIN_TEST_USER}; use kanidmd_testkit::{create_user, ADMIN_TEST_PASSWORD, ADMIN_TEST_USER};
use serde_json::Value; use serde_json::Value;