mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 12:37:00 +01:00
Chore: Refactor Groups to be more generic (#3136)
This commit is contained in:
parent
d2ae2ca206
commit
dc56a3217d
|
@ -166,6 +166,15 @@ pub struct EntryReduced {
|
|||
// Today is that day - @firstyear
|
||||
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 {
|
||||
// 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<Uuid> {
|
||||
pub fn get_uuid(&self) -> Option<Uuid> {
|
||||
self.attrs
|
||||
.get(&Attribute::Uuid)
|
||||
.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>
|
||||
where
|
||||
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> {
|
||||
pub fn get_uuid(&self) -> Uuid {
|
||||
self.valid.uuid
|
||||
|
|
|
@ -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<Credential>,
|
||||
shell: Option<String>,
|
||||
gidnumber: u32,
|
||||
groups: Vec<UnixGroup>,
|
||||
groups: Vec<Group<Unix>>,
|
||||
}
|
||||
|
||||
impl UnixExtensions {
|
||||
|
@ -54,7 +51,7 @@ pub struct Account {
|
|||
pub displayname: String,
|
||||
pub uuid: Uuid,
|
||||
pub sync_parent_uuid: Option<Uuid>,
|
||||
pub groups: Vec<Group>,
|
||||
pub groups: Vec<Group<()>>,
|
||||
pub primary: Option<Credential>,
|
||||
pub passkeys: BTreeMap<Uuid, (String, PasskeyV4)>,
|
||||
pub attested_passkeys: BTreeMap<Uuid, (String, AttestedPasskeyV4)>,
|
||||
|
@ -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<EntrySealed, EntryCommitted>,
|
||||
qs: &mut QueryServerReadTransaction,
|
||||
) -> 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)
|
||||
}
|
||||
|
@ -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<EntrySealed, EntryCommitted>,
|
||||
qs: &mut QueryServerWriteTransaction,
|
||||
) -> 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)
|
||||
}
|
||||
|
@ -269,7 +266,7 @@ impl Account {
|
|||
value: &Entry<EntryReduced, EntryCommitted>,
|
||||
qs: &mut QueryServerReadTransaction,
|
||||
) -> 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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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<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))
|
||||
}
|
||||
// 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<T>
|
||||
where
|
||||
T: GroupType,
|
||||
{
|
||||
inner: T,
|
||||
spn: String,
|
||||
uuid: Uuid,
|
||||
// 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 {
|
||||
($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<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)
|
||||
.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<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 {
|
||||
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<EntrySealed, EntryCommitted>,
|
||||
qs: &mut TXN,
|
||||
) -> Result<Vec<Group<()>>, 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<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(
|
||||
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<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)
|
||||
Ok(std::iter::once(user_group)
|
||||
.chain(Self::try_from_account_reduced(value, qs)?)
|
||||
.collect())
|
||||
}
|
||||
|
||||
pub(crate) fn try_from_entry_reduced(
|
||||
value: &Entry<EntryReduced, EntryCommitted>,
|
||||
) -> Result<Self, OperationError> {
|
||||
try_from_group_e!(value)
|
||||
pub fn try_from_account_reduced<'a, E, TXN>(
|
||||
value: &Entry<E, EntryCommitted>,
|
||||
qs: &mut TXN,
|
||||
) -> Result<Vec<Group<Unix>>, OperationError>
|
||||
where
|
||||
E: Committed,
|
||||
TXN: QueryServerTransaction<'a>,
|
||||
{
|
||||
try_from_account!(value, qs)
|
||||
}
|
||||
|
||||
pub(crate) fn try_from_entry(
|
||||
value: &Entry<EntrySealed, EntryCommitted>,
|
||||
) -> Result<Self, OperationError> {
|
||||
try_from_group_e!(value)
|
||||
fn check_entry_classes<E>(value: &Entry<E, EntryCommitted>) -> Result<(), OperationError>
|
||||
where
|
||||
E: Committed,
|
||||
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 {
|
||||
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<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))
|
||||
}
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -13,7 +13,7 @@ pub(crate) struct RadiusAccount {
|
|||
pub name: String,
|
||||
pub displayname: String,
|
||||
pub uuid: Uuid,
|
||||
pub groups: Vec<Group>,
|
||||
pub groups: Vec<Group<()>>,
|
||||
pub radius_secret: String,
|
||||
pub valid_from: Option<OffsetDateTime>,
|
||||
pub expire: Option<OffsetDateTime>,
|
||||
|
@ -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);
|
||||
|
||||
|
|
|
@ -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::<Unix>::try_from_entry(&e))
|
||||
.map_err(|e| {
|
||||
admin_error!("Failed to start unix group token {:?}", e);
|
||||
e
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in a new issue