use crate::audit::AuditScope;
use crate::entry::{Entry, EntryCommitted, EntryInvalid, EntrySealed};
use crate::event::{CreateEvent, DeleteEvent, ModifyEvent};
use crate::plugins::Plugin;
use crate::server::QueryServerTransaction;
use crate::server::{QueryServerReadTransaction, QueryServerWriteTransaction};
use crate::value::{PartialValue, Value};
use kanidm_proto::v1::{ConsistencyError, OperationError};
use hashbrown::HashMap;
use std::collections::BTreeSet;
use uuid::Uuid;
lazy_static! {
static ref CLASS_GROUP: PartialValue = PartialValue::new_class("group");
static ref CLASS_MEMBEROF: Value = Value::new_class("memberof");
}
pub struct MemberOf;
type EntrySealedCommitted = Entry<EntrySealed, EntryCommitted>;
type EntryInvalidCommitted = Entry<EntryInvalid, EntryCommitted>;
type EntryTuple = (EntrySealedCommitted, EntryInvalidCommitted);
fn do_memberof(
au: &mut AuditScope,
qs: &QueryServerWriteTransaction,
uuid: &Uuid,
tgte: &mut EntryInvalidCommitted,
) -> Result<(), OperationError> {
let groups = qs
.internal_search(
au,
filter!(f_and!([
f_eq("class", CLASS_GROUP.clone()),
f_eq("member", PartialValue::new_refer(*uuid))
])),
)
.map_err(|e| {
ladmin_error!(au, "internal search failure -> {:?}", e);
e
})?;
tgte.add_ava("class", CLASS_MEMBEROF.clone());
tgte.pop_ava("memberof");
tgte.pop_ava("directmemberof");
groups.iter().for_each(|g| {
let dmo = Value::new_refer(*g.get_uuid());
tgte.add_ava("directmemberof", dmo.clone());
tgte.add_ava("memberof", dmo);
if let Some(miter) = g.get_ava("memberof") {
miter.for_each(|mo| {
tgte.add_ava("memberof", mo.clone());
})
};
});
ltrace!(
au,
"Updating {:?} to be dir mo {:?}",
uuid,
tgte.get_ava_set("directmemberof")
);
ltrace!(
au,
"Updating {:?} to be mo {:?}",
uuid,
tgte.get_ava_set("memberof")
);
Ok(())
}
#[allow(clippy::cognitive_complexity)]
fn apply_memberof(
au: &mut AuditScope,
qs: &QueryServerWriteTransaction,
mut group_affect: Vec<Uuid>,
) -> Result<(), OperationError> {
ltrace!(au, " => entering apply_memberof");
ltrace!(au, " => initial group_affect {:?}", group_affect);
let mut other_cache: HashMap<Uuid, EntryTuple> = HashMap::with_capacity(group_affect.len() * 2);
while !group_affect.is_empty() {
group_affect.sort();
group_affect.dedup();
let mut pre_candidates = Vec::with_capacity(group_affect.len());
let mut candidates = Vec::with_capacity(group_affect.len());
let filt = filter!(FC::Or(
group_affect
.drain(0..)
.map(|u| f_eq("uuid", PartialValue::new_uuid(u)))
.collect()
));
let mut work_set = qs.internal_search_writeable(au, filt)?;
while let Some((pre, mut tgte)) = work_set.pop() {
let guuid = *pre.get_uuid();
if !tgte.attribute_value_pres("class", &CLASS_GROUP) {
ltrace!(au, "not a group, delaying update to -> {:?}", guuid);
other_cache.insert(guuid, (pre, tgte));
continue;
}
ltrace!(au, "=> processing group update -> {:?}", guuid);
do_memberof(au, qs, &guuid, &mut tgte)?;
if pre.get_ava_set("memberof") != tgte.get_ava_set("memberof")
|| pre.get_ava_set("directmemberof") != tgte.get_ava_set("directmemberof")
{
ltrace!(
au,
"{:?} changed, flagging members as groups to change. ",
guuid
);
if let Some(miter) = tgte.get_ava_as_refuuid("member") {
group_affect.extend(miter.filter(|m| !other_cache.contains_key(m)));
};
pre_candidates.push(pre);
candidates.push(tgte);
} else {
ltrace!(au, "{:?} stable", guuid);
}
}
debug_assert!(pre_candidates.len() == candidates.len());
if !pre_candidates.is_empty() {
qs.internal_batch_modify(au, pre_candidates, candidates)
.map_err(|e| {
ladmin_error!(au, "Failed to commit memberof group set {:?}", e);
e
})?;
}
}
let mut pre_candidates = Vec::with_capacity(other_cache.len());
let mut candidates = Vec::with_capacity(other_cache.len());
other_cache
.into_iter()
.try_for_each(|(auuid, (pre, mut tgte))| {
ltrace!(au, "=> processing affected uuid {:?}", auuid);
debug_assert!(!tgte.attribute_value_pres("class", &CLASS_GROUP));
do_memberof(au, qs, &auuid, &mut tgte)?;
if pre.get_ava_set("memberof") != tgte.get_ava_set("memberof")
|| pre.get_ava_set("directmemberof") != tgte.get_ava_set("directmemberof")
{
pre_candidates.push(pre);
candidates.push(tgte);
}
Ok(())
})?;
qs.internal_batch_modify(au, pre_candidates, candidates)
}
impl Plugin for MemberOf {
fn id() -> &'static str {
"memberof"
}
fn post_create(
au: &mut AuditScope,
qs: &QueryServerWriteTransaction,
cand: &[Entry<EntrySealed, EntryCommitted>],
_ce: &CreateEvent,
) -> Result<(), OperationError> {
let group_affect = cand
.iter()
.map(|e| e.get_uuid())
.chain(
cand.iter()
.filter_map(|e| {
if e.attribute_value_pres("class", &CLASS_GROUP) {
e.get_ava_as_refuuid("member")
} else {
None
}
})
.flatten(),
)
.copied()
.collect();
apply_memberof(au, qs, group_affect)
}
fn post_modify(
au: &mut AuditScope,
qs: &QueryServerWriteTransaction,
pre_cand: &[Entry<EntrySealed, EntryCommitted>],
cand: &[Entry<EntrySealed, EntryCommitted>],
_me: &ModifyEvent,
) -> Result<(), OperationError> {
let group_affect = cand
.iter()
.map(|post| post.get_uuid())
.chain(
pre_cand
.iter()
.filter_map(|pre| {
if pre.attribute_value_pres("class", &CLASS_GROUP) {
pre.get_ava_as_refuuid("member")
} else {
None
}
})
.flatten(),
)
.chain(
cand.iter()
.filter_map(|post| {
if post.attribute_value_pres("class", &CLASS_GROUP) {
post.get_ava_as_refuuid("member")
} else {
None
}
})
.flatten(),
)
.copied()
.collect();
apply_memberof(au, qs, group_affect)
}
fn pre_delete(
_au: &mut AuditScope,
_qs: &QueryServerWriteTransaction,
cand: &mut Vec<Entry<EntryInvalid, EntryCommitted>>,
_de: &DeleteEvent,
) -> Result<(), OperationError> {
cand.iter_mut().for_each(|e| e.purge_ava("memberof"));
Ok(())
}
fn post_delete(
au: &mut AuditScope,
qs: &QueryServerWriteTransaction,
cand: &[Entry<EntrySealed, EntryCommitted>],
_ce: &DeleteEvent,
) -> Result<(), OperationError> {
let group_affect = cand
.iter()
.filter_map(|e| {
if e.attribute_value_pres("class", &CLASS_GROUP) {
e.get_ava_as_refuuid("member")
} else {
None
}
})
.flatten()
.copied()
.collect();
apply_memberof(au, qs, group_affect)
}
fn verify(
au: &mut AuditScope,
qs: &QueryServerReadTransaction,
) -> Vec<Result<(), ConsistencyError>> {
let mut r = Vec::new();
let filt_in = filter!(f_pres("class"));
let all_cand = match qs
.internal_search(au, filt_in)
.map_err(|_| Err(ConsistencyError::QueryServerSearchFailure))
{
Ok(all_cand) => all_cand,
Err(e) => return vec![e],
};
for e in all_cand {
let filt_in = filter!(f_and!([
f_eq("class", PartialValue::new_class("group")),
f_eq("member", PartialValue::new_refer(*e.get_uuid()))
]));
let direct_memberof = match qs
.internal_search(au, filt_in)
.map_err(|_| ConsistencyError::QueryServerSearchFailure)
{
Ok(d_mo) => d_mo,
Err(e) => return vec![Err(e)],
};
let d_groups_set: BTreeSet<_> = direct_memberof
.iter()
.map(|e| Value::new_refer(*e.get_uuid()))
.collect();
let d_groups_set = if d_groups_set.is_empty() {
None
} else {
Some(d_groups_set)
};
ltrace!(
au,
"DMO search groups {:?} -> {:?}",
e.get_uuid(),
d_groups_set
);
match (e.get_ava_set("directmemberof"), d_groups_set) {
(Some(edmos), Some(dmos)) => {
let diff: Vec<_> = dmos.symmetric_difference(edmos).collect();
if !diff.is_empty() {
ladmin_error!(
au,
"MemberOfInvalid: Entry {}, DMO has inconsistencies -> {:?}",
e,
diff
);
r.push(Err(ConsistencyError::MemberOfInvalid(e.get_id())));
}
}
(None, None) => {
}
_ => {
ladmin_error!(
au,
"MemberOfInvalid directmemberof set and DMO search set differ in size: {}",
e.get_uuid()
);
r.push(Err(ConsistencyError::MemberOfInvalid(e.get_id())));
}
}
}
r
}
}
#[cfg(test)]
mod tests {
use crate::entry::{Entry, EntryInit, EntryNew};
use crate::modify::{Modify, ModifyList};
use crate::server::{QueryServerTransaction, QueryServerWriteTransaction};
use crate::value::{PartialValue, Value};
const UUID_A: &'static str = "aaaaaaaa-f82e-4484-a407-181aa03bda5c";
const UUID_B: &'static str = "bbbbbbbb-2438-4384-9891-48f4c8172e9b";
const UUID_C: &'static str = "cccccccc-9b01-423f-9ba6-51aa4bbd5dd2";
const UUID_D: &'static str = "dddddddd-2ab3-48e3-938d-1b4754cd2984";
const EA: &'static str = r#"{
"attrs": {
"class": ["group", "memberof"],
"name": ["testgroup_a"],
"uuid": ["aaaaaaaa-f82e-4484-a407-181aa03bda5c"]
}
}"#;
const EB: &'static str = r#"{
"attrs": {
"class": ["group", "memberof"],
"name": ["testgroup_b"],
"uuid": ["bbbbbbbb-2438-4384-9891-48f4c8172e9b"]
}
}"#;
const EC: &'static str = r#"{
"attrs": {
"class": ["group", "memberof"],
"name": ["testgroup_c"],
"uuid": ["cccccccc-9b01-423f-9ba6-51aa4bbd5dd2"]
}
}"#;
const ED: &'static str = r#"{
"attrs": {
"class": ["group", "memberof"],
"name": ["testgroup_d"],
"uuid": ["dddddddd-2ab3-48e3-938d-1b4754cd2984"]
}
}"#;
macro_rules! assert_memberof_int {
(
$au:expr,
$qs:expr,
$ea:expr,
$eb:expr,
$mo:expr,
$cand:expr
) => {{
let filt = filter!(f_and!([
f_eq("uuid", PartialValue::new_uuids($ea).unwrap()),
f_eq($mo, PartialValue::new_refer_s($eb).unwrap())
]));
let cands = $qs
.internal_search($au, filt)
.expect("Internal search failure");
debug!("assert_mo_cands {:?}", cands);
assert!(cands.len() == $cand);
}};
}
macro_rules! assert_memberof {
(
$au:expr,
$qs:expr,
$ea:expr,
$eb:expr
) => {{
assert_memberof_int!($au, $qs, $ea, $eb, "memberof", 1);
}};
}
macro_rules! assert_dirmemberof {
(
$au:expr,
$qs:expr,
$ea:expr,
$eb:expr
) => {{
assert_memberof_int!($au, $qs, $ea, $eb, "directmemberof", 1);
}};
}
macro_rules! assert_not_memberof {
(
$au:expr,
$qs:expr,
$ea:expr,
$eb:expr
) => {{
assert_memberof_int!($au, $qs, $ea, $eb, "memberof", 0);
}};
}
macro_rules! assert_not_dirmemberof {
(
$au:expr,
$qs:expr,
$ea:expr,
$eb:expr
) => {{
assert_memberof_int!($au, $qs, $ea, $eb, "directmemberof", 0);
}};
}
#[test]
fn test_create_mo_single() {
let mut ea: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EA);
let eb: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EB);
ea.add_ava("member", Value::new_refer_s(&UUID_B).unwrap());
let preload = Vec::new();
let create = vec![ea, eb];
run_create_test!(
Ok(()),
preload,
create,
None,
|au: &mut AuditScope, qs: &QueryServerWriteTransaction| {
assert_memberof!(au, qs, UUID_B, UUID_A);
assert_not_memberof!(au, qs, UUID_A, UUID_B);
assert_dirmemberof!(au, qs, UUID_B, UUID_A);
assert_not_dirmemberof!(au, qs, UUID_A, UUID_B);
}
);
}
#[test]
fn test_create_mo_nested() {
let mut ea: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EA);
let mut eb: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EB);
let ec: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EC);
ea.add_ava("member", Value::new_refer_s(&UUID_B).unwrap());
eb.add_ava("member", Value::new_refer_s(&UUID_C).unwrap());
let preload = Vec::new();
let create = vec![ea, eb, ec];
run_create_test!(
Ok(()),
preload,
create,
None,
|au: &mut AuditScope, qs: &QueryServerWriteTransaction| {
assert_not_memberof!(au, qs, UUID_A, UUID_A);
assert_not_memberof!(au, qs, UUID_A, UUID_B);
assert_not_memberof!(au, qs, UUID_A, UUID_C);
assert_memberof!(au, qs, UUID_B, UUID_A);
assert_not_memberof!(au, qs, UUID_B, UUID_B);
assert_not_memberof!(au, qs, UUID_B, UUID_C);
assert_memberof!(au, qs, UUID_C, UUID_A);
assert_memberof!(au, qs, UUID_C, UUID_B);
assert_not_memberof!(au, qs, UUID_C, UUID_C);
assert_not_dirmemberof!(au, qs, UUID_A, UUID_A);
assert_not_dirmemberof!(au, qs, UUID_A, UUID_B);
assert_not_dirmemberof!(au, qs, UUID_A, UUID_C);
assert_dirmemberof!(au, qs, UUID_B, UUID_A);
assert_not_dirmemberof!(au, qs, UUID_B, UUID_B);
assert_not_dirmemberof!(au, qs, UUID_B, UUID_C);
assert_not_dirmemberof!(au, qs, UUID_C, UUID_A);
assert_dirmemberof!(au, qs, UUID_C, UUID_B);
assert_not_dirmemberof!(au, qs, UUID_C, UUID_C);
}
);
}
#[test]
fn test_create_mo_cycle() {
let mut ea: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EA);
let mut eb: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EB);
let mut ec: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EC);
ea.add_ava("member", Value::new_refer_s(&UUID_B).unwrap());
eb.add_ava("member", Value::new_refer_s(&UUID_C).unwrap());
ec.add_ava("member", Value::new_refer_s(&UUID_A).unwrap());
let preload = Vec::new();
let create = vec![ea, eb, ec];
run_create_test!(
Ok(()),
preload,
create,
None,
|au: &mut AuditScope, qs: &QueryServerWriteTransaction| {
assert_memberof!(au, qs, UUID_A, UUID_A);
assert_memberof!(au, qs, UUID_A, UUID_B);
assert_memberof!(au, qs, UUID_A, UUID_C);
assert_memberof!(au, qs, UUID_B, UUID_A);
assert_memberof!(au, qs, UUID_B, UUID_B);
assert_memberof!(au, qs, UUID_B, UUID_C);
assert_memberof!(au, qs, UUID_C, UUID_A);
assert_memberof!(au, qs, UUID_C, UUID_B);
assert_memberof!(au, qs, UUID_C, UUID_C);
assert_not_dirmemberof!(au, qs, UUID_A, UUID_A);
assert_not_dirmemberof!(au, qs, UUID_A, UUID_B);
assert_dirmemberof!(au, qs, UUID_A, UUID_C);
assert_dirmemberof!(au, qs, UUID_B, UUID_A);
assert_not_dirmemberof!(au, qs, UUID_B, UUID_B);
assert_not_dirmemberof!(au, qs, UUID_B, UUID_C);
assert_not_dirmemberof!(au, qs, UUID_C, UUID_A);
assert_dirmemberof!(au, qs, UUID_C, UUID_B);
assert_not_dirmemberof!(au, qs, UUID_C, UUID_C);
}
);
}
#[test]
fn test_create_mo_multi_cycle() {
let mut ea: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EA);
let mut eb: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EB);
let mut ec: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EC);
let mut ed: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(ED);
ea.add_ava("member", Value::new_refer_s(&UUID_B).unwrap());
eb.add_ava("member", Value::new_refer_s(&UUID_C).unwrap());
ec.add_ava("member", Value::new_refer_s(&UUID_A).unwrap());
ec.add_ava("member", Value::new_refer_s(&UUID_D).unwrap());
ed.add_ava("member", Value::new_refer_s(&UUID_A).unwrap());
let preload = Vec::new();
let create = vec![ea, eb, ec, ed];
run_create_test!(
Ok(()),
preload,
create,
None,
|au: &mut AuditScope, qs: &QueryServerWriteTransaction| {
assert_memberof!(au, qs, UUID_A, UUID_A);
assert_memberof!(au, qs, UUID_A, UUID_B);
assert_memberof!(au, qs, UUID_A, UUID_C);
assert_memberof!(au, qs, UUID_A, UUID_D);
assert_memberof!(au, qs, UUID_B, UUID_A);
assert_memberof!(au, qs, UUID_B, UUID_B);
assert_memberof!(au, qs, UUID_B, UUID_C);
assert_memberof!(au, qs, UUID_B, UUID_D);
assert_memberof!(au, qs, UUID_C, UUID_A);
assert_memberof!(au, qs, UUID_C, UUID_B);
assert_memberof!(au, qs, UUID_C, UUID_C);
assert_memberof!(au, qs, UUID_C, UUID_D);
assert_memberof!(au, qs, UUID_D, UUID_A);
assert_memberof!(au, qs, UUID_D, UUID_B);
assert_memberof!(au, qs, UUID_D, UUID_C);
assert_memberof!(au, qs, UUID_D, UUID_D);
assert_not_dirmemberof!(au, qs, UUID_A, UUID_A);
assert_not_dirmemberof!(au, qs, UUID_A, UUID_B);
assert_dirmemberof!(au, qs, UUID_A, UUID_C);
assert_dirmemberof!(au, qs, UUID_A, UUID_D);
assert_dirmemberof!(au, qs, UUID_B, UUID_A);
assert_not_dirmemberof!(au, qs, UUID_B, UUID_B);
assert_not_dirmemberof!(au, qs, UUID_B, UUID_C);
assert_not_dirmemberof!(au, qs, UUID_B, UUID_D);
assert_not_dirmemberof!(au, qs, UUID_C, UUID_A);
assert_dirmemberof!(au, qs, UUID_C, UUID_B);
assert_not_dirmemberof!(au, qs, UUID_C, UUID_C);
assert_not_dirmemberof!(au, qs, UUID_C, UUID_D);
assert_not_dirmemberof!(au, qs, UUID_D, UUID_A);
assert_not_dirmemberof!(au, qs, UUID_D, UUID_B);
assert_dirmemberof!(au, qs, UUID_D, UUID_C);
assert_not_dirmemberof!(au, qs, UUID_D, UUID_D);
}
);
}
#[test]
fn test_modify_mo_add_simple() {
let ea: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EA);
let eb: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EB);
let preload = vec![ea, eb];
run_modify_test!(
Ok(()),
preload,
filter!(f_eq("uuid", PartialValue::new_uuids(&UUID_A).unwrap())),
ModifyList::new_list(vec![Modify::Present(
"member".to_string(),
Value::new_refer_s(&UUID_B).unwrap()
)]),
None,
|au: &mut AuditScope, qs: &QueryServerWriteTransaction| {
assert_memberof!(au, qs, UUID_B, UUID_A);
assert_not_memberof!(au, qs, UUID_A, UUID_B);
assert_dirmemberof!(au, qs, UUID_B, UUID_A);
assert_not_dirmemberof!(au, qs, UUID_A, UUID_B);
}
);
}
#[test]
fn test_modify_mo_add_nested_1() {
let ea: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EA);
let mut eb: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EB);
let ec: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EC);
eb.add_ava("member", Value::new_refer_s(&UUID_C).unwrap());
let preload = vec![ea, eb, ec];
run_modify_test!(
Ok(()),
preload,
filter!(f_eq("uuid", PartialValue::new_uuids(&UUID_A).unwrap())),
ModifyList::new_list(vec![Modify::Present(
"member".to_string(),
Value::new_refer_s(&UUID_B).unwrap()
)]),
None,
|au: &mut AuditScope, qs: &QueryServerWriteTransaction| {
assert_not_memberof!(au, qs, UUID_A, UUID_A);
assert_not_memberof!(au, qs, UUID_A, UUID_B);
assert_not_memberof!(au, qs, UUID_A, UUID_C);
assert_memberof!(au, qs, UUID_B, UUID_A);
assert_not_memberof!(au, qs, UUID_B, UUID_B);
assert_not_memberof!(au, qs, UUID_B, UUID_C);
assert_memberof!(au, qs, UUID_C, UUID_A);
assert_memberof!(au, qs, UUID_C, UUID_B);
assert_not_memberof!(au, qs, UUID_C, UUID_C);
assert_not_dirmemberof!(au, qs, UUID_A, UUID_A);
assert_not_dirmemberof!(au, qs, UUID_A, UUID_B);
assert_not_dirmemberof!(au, qs, UUID_A, UUID_C);
assert_dirmemberof!(au, qs, UUID_B, UUID_A);
assert_not_dirmemberof!(au, qs, UUID_B, UUID_B);
assert_not_dirmemberof!(au, qs, UUID_B, UUID_C);
assert_not_dirmemberof!(au, qs, UUID_C, UUID_A);
assert_dirmemberof!(au, qs, UUID_C, UUID_B);
assert_not_dirmemberof!(au, qs, UUID_C, UUID_C);
}
);
}
#[test]
fn test_modify_mo_add_nested_2() {
let mut ea: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EA);
let eb: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EB);
let ec: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EC);
ea.add_ava("member", Value::new_refer_s(&UUID_B).unwrap());
let preload = vec![ea, eb, ec];
run_modify_test!(
Ok(()),
preload,
filter!(f_eq("uuid", PartialValue::new_uuids(&UUID_B).unwrap())),
ModifyList::new_list(vec![Modify::Present(
"member".to_string(),
Value::new_refer_s(&UUID_C).unwrap()
)]),
None,
|au: &mut AuditScope, qs: &QueryServerWriteTransaction| {
assert_not_memberof!(au, qs, UUID_A, UUID_A);
assert_not_memberof!(au, qs, UUID_A, UUID_B);
assert_not_memberof!(au, qs, UUID_A, UUID_C);
assert_memberof!(au, qs, UUID_B, UUID_A);
assert_not_memberof!(au, qs, UUID_B, UUID_B);
assert_not_memberof!(au, qs, UUID_B, UUID_C);
assert_memberof!(au, qs, UUID_C, UUID_A);
assert_memberof!(au, qs, UUID_C, UUID_B);
assert_not_memberof!(au, qs, UUID_C, UUID_C);
assert_not_dirmemberof!(au, qs, UUID_A, UUID_A);
assert_not_dirmemberof!(au, qs, UUID_A, UUID_B);
assert_not_dirmemberof!(au, qs, UUID_A, UUID_C);
assert_dirmemberof!(au, qs, UUID_B, UUID_A);
assert_not_dirmemberof!(au, qs, UUID_B, UUID_B);
assert_not_dirmemberof!(au, qs, UUID_B, UUID_C);
assert_not_dirmemberof!(au, qs, UUID_C, UUID_A);
assert_dirmemberof!(au, qs, UUID_C, UUID_B);
assert_not_dirmemberof!(au, qs, UUID_C, UUID_C);
}
);
}
#[test]
fn test_modify_mo_add_cycle() {
let mut ea: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EA);
let mut eb: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EB);
let ec: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EC);
ea.add_ava("member", Value::new_refer_s(&UUID_B).unwrap());
eb.add_ava("member", Value::new_refer_s(&UUID_C).unwrap());
let preload = vec![ea, eb, ec];
run_modify_test!(
Ok(()),
preload,
filter!(f_eq("uuid", PartialValue::new_uuids(&UUID_C).unwrap())),
ModifyList::new_list(vec![Modify::Present(
"member".to_string(),
Value::new_refer_s(&UUID_A).unwrap()
)]),
None,
|au: &mut AuditScope, qs: &QueryServerWriteTransaction| {
assert_memberof!(au, qs, UUID_A, UUID_A);
assert_memberof!(au, qs, UUID_A, UUID_B);
assert_memberof!(au, qs, UUID_A, UUID_C);
assert_memberof!(au, qs, UUID_B, UUID_A);
assert_memberof!(au, qs, UUID_B, UUID_B);
assert_memberof!(au, qs, UUID_B, UUID_C);
assert_memberof!(au, qs, UUID_C, UUID_A);
assert_memberof!(au, qs, UUID_C, UUID_B);
assert_memberof!(au, qs, UUID_C, UUID_C);
assert_not_dirmemberof!(au, qs, UUID_A, UUID_A);
assert_not_dirmemberof!(au, qs, UUID_A, UUID_B);
assert_dirmemberof!(au, qs, UUID_A, UUID_C);
assert_dirmemberof!(au, qs, UUID_B, UUID_A);
assert_not_dirmemberof!(au, qs, UUID_B, UUID_B);
assert_not_dirmemberof!(au, qs, UUID_B, UUID_C);
assert_not_dirmemberof!(au, qs, UUID_C, UUID_A);
assert_dirmemberof!(au, qs, UUID_C, UUID_B);
assert_not_dirmemberof!(au, qs, UUID_C, UUID_C);
}
);
}
#[test]
fn test_modify_mo_add_multi_cycle() {
let mut ea: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EA);
let mut eb: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EB);
let mut ec: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EC);
let ed: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(ED);
ea.add_ava("member", Value::new_refer_s(&UUID_B).unwrap());
eb.add_ava("member", Value::new_refer_s(&UUID_C).unwrap());
ec.add_ava("member", Value::new_refer_s(&UUID_D).unwrap());
let preload = vec![ea, eb, ec, ed];
run_modify_test!(
Ok(()),
preload,
filter!(f_or!([
f_eq("uuid", PartialValue::new_uuids(&UUID_C).unwrap()),
f_eq("uuid", PartialValue::new_uuids(&UUID_D).unwrap()),
])),
ModifyList::new_list(vec![Modify::Present(
"member".to_string(),
Value::new_refer_s(&UUID_A).unwrap()
)]),
None,
|au: &mut AuditScope, qs: &QueryServerWriteTransaction| {
assert_memberof!(au, qs, UUID_A, UUID_A);
assert_memberof!(au, qs, UUID_A, UUID_B);
assert_memberof!(au, qs, UUID_A, UUID_C);
assert_memberof!(au, qs, UUID_A, UUID_D);
assert_memberof!(au, qs, UUID_B, UUID_A);
assert_memberof!(au, qs, UUID_B, UUID_B);
assert_memberof!(au, qs, UUID_B, UUID_C);
assert_memberof!(au, qs, UUID_B, UUID_D);
assert_memberof!(au, qs, UUID_C, UUID_A);
assert_memberof!(au, qs, UUID_C, UUID_B);
assert_memberof!(au, qs, UUID_C, UUID_C);
assert_memberof!(au, qs, UUID_C, UUID_D);
assert_memberof!(au, qs, UUID_D, UUID_A);
assert_memberof!(au, qs, UUID_D, UUID_B);
assert_memberof!(au, qs, UUID_D, UUID_C);
assert_memberof!(au, qs, UUID_D, UUID_D);
assert_not_dirmemberof!(au, qs, UUID_A, UUID_A);
assert_not_dirmemberof!(au, qs, UUID_A, UUID_B);
assert_dirmemberof!(au, qs, UUID_A, UUID_C);
assert_dirmemberof!(au, qs, UUID_A, UUID_D);
assert_dirmemberof!(au, qs, UUID_B, UUID_A);
assert_not_dirmemberof!(au, qs, UUID_B, UUID_B);
assert_not_dirmemberof!(au, qs, UUID_B, UUID_C);
assert_not_dirmemberof!(au, qs, UUID_B, UUID_D);
assert_not_dirmemberof!(au, qs, UUID_C, UUID_A);
assert_dirmemberof!(au, qs, UUID_C, UUID_B);
assert_not_dirmemberof!(au, qs, UUID_C, UUID_C);
assert_not_dirmemberof!(au, qs, UUID_C, UUID_D);
assert_not_dirmemberof!(au, qs, UUID_D, UUID_A);
assert_not_dirmemberof!(au, qs, UUID_D, UUID_B);
assert_dirmemberof!(au, qs, UUID_D, UUID_C);
assert_not_dirmemberof!(au, qs, UUID_D, UUID_D);
}
);
}
#[test]
fn test_modify_mo_del_simple() {
let mut ea: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EA);
let mut eb: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EB);
ea.add_ava("member", Value::new_refer_s(&UUID_B).unwrap());
eb.add_ava("memberof", Value::new_refer_s(&UUID_A).unwrap());
let preload = vec![ea, eb];
run_modify_test!(
Ok(()),
preload,
filter!(f_eq("uuid", PartialValue::new_uuids(&UUID_A).unwrap())),
ModifyList::new_list(vec![Modify::Removed(
"member".to_string(),
PartialValue::new_refer_s(&UUID_B).unwrap()
)]),
None,
|au: &mut AuditScope, qs: &QueryServerWriteTransaction| {
assert_not_memberof!(au, qs, UUID_B, UUID_A);
assert_not_memberof!(au, qs, UUID_A, UUID_B);
assert_not_dirmemberof!(au, qs, UUID_B, UUID_A);
assert_not_dirmemberof!(au, qs, UUID_A, UUID_B);
}
);
}
#[test]
fn test_modify_mo_del_nested_1() {
let mut ea: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EA);
let mut eb: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EB);
let mut ec: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EC);
ea.add_ava("member", Value::new_refer_s(&UUID_B).unwrap());
eb.add_ava("memberof", Value::new_refer_s(&UUID_A).unwrap());
eb.add_ava("member", Value::new_refer_s(&UUID_C).unwrap());
ec.add_ava("memberof", Value::new_refer_s(&UUID_B).unwrap());
let preload = vec![ea, eb, ec];
run_modify_test!(
Ok(()),
preload,
filter!(f_eq("uuid", PartialValue::new_uuids(&UUID_A).unwrap())),
ModifyList::new_list(vec![Modify::Removed(
"member".to_string(),
PartialValue::new_refer_s(&UUID_B).unwrap()
)]),
None,
|au: &mut AuditScope, qs: &QueryServerWriteTransaction| {
assert_not_memberof!(au, qs, UUID_A, UUID_A);
assert_not_memberof!(au, qs, UUID_A, UUID_B);
assert_not_memberof!(au, qs, UUID_A, UUID_C);
assert_not_memberof!(au, qs, UUID_B, UUID_A);
assert_not_memberof!(au, qs, UUID_B, UUID_B);
assert_not_memberof!(au, qs, UUID_B, UUID_C);
assert_not_memberof!(au, qs, UUID_C, UUID_A);
assert_memberof!(au, qs, UUID_C, UUID_B);
assert_not_memberof!(au, qs, UUID_C, UUID_C);
assert_not_dirmemberof!(au, qs, UUID_A, UUID_A);
assert_not_dirmemberof!(au, qs, UUID_A, UUID_B);
assert_not_dirmemberof!(au, qs, UUID_A, UUID_C);
assert_not_dirmemberof!(au, qs, UUID_B, UUID_A);
assert_not_dirmemberof!(au, qs, UUID_B, UUID_B);
assert_not_dirmemberof!(au, qs, UUID_B, UUID_C);
assert_not_dirmemberof!(au, qs, UUID_C, UUID_A);
assert_dirmemberof!(au, qs, UUID_C, UUID_B);
assert_not_dirmemberof!(au, qs, UUID_C, UUID_C);
}
);
}
#[test]
fn test_modify_mo_del_nested_2() {
let mut ea: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EA);
let mut eb: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EB);
let mut ec: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EC);
ea.add_ava("member", Value::new_refer_s(&UUID_B).unwrap());
eb.add_ava("memberof", Value::new_refer_s(&UUID_A).unwrap());
eb.add_ava("member", Value::new_refer_s(&UUID_C).unwrap());
ec.add_ava("memberof", Value::new_refer_s(&UUID_B).unwrap());
ec.add_ava("memberof", Value::new_refer_s(&UUID_A).unwrap());
let preload = vec![ea, eb, ec];
run_modify_test!(
Ok(()),
preload,
filter!(f_eq("uuid", PartialValue::new_uuids(&UUID_B).unwrap())),
ModifyList::new_list(vec![Modify::Removed(
"member".to_string(),
PartialValue::new_refer_s(&UUID_C).unwrap()
)]),
None,
|au: &mut AuditScope, qs: &QueryServerWriteTransaction| {
assert_not_memberof!(au, qs, UUID_A, UUID_A);
assert_not_memberof!(au, qs, UUID_A, UUID_B);
assert_not_memberof!(au, qs, UUID_A, UUID_C);
assert_memberof!(au, qs, UUID_B, UUID_A);
assert_not_memberof!(au, qs, UUID_B, UUID_B);
assert_not_memberof!(au, qs, UUID_B, UUID_C);
assert_not_memberof!(au, qs, UUID_C, UUID_A);
assert_not_memberof!(au, qs, UUID_C, UUID_B);
assert_not_memberof!(au, qs, UUID_C, UUID_C);
assert_not_dirmemberof!(au, qs, UUID_A, UUID_A);
assert_not_dirmemberof!(au, qs, UUID_A, UUID_B);
assert_not_dirmemberof!(au, qs, UUID_A, UUID_C);
assert_dirmemberof!(au, qs, UUID_B, UUID_A);
assert_not_dirmemberof!(au, qs, UUID_B, UUID_B);
assert_not_dirmemberof!(au, qs, UUID_B, UUID_C);
assert_not_dirmemberof!(au, qs, UUID_C, UUID_A);
assert_not_dirmemberof!(au, qs, UUID_C, UUID_B);
assert_not_dirmemberof!(au, qs, UUID_C, UUID_C);
}
);
}
#[test]
fn test_modify_mo_del_cycle() {
let mut ea: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EA);
let mut eb: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EB);
let mut ec: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EC);
ea.add_ava("member", Value::new_refer_s(&UUID_B).unwrap());
ea.add_ava("memberof", Value::new_refer_s(&UUID_C).unwrap());
ea.add_ava("memberof", Value::new_refer_s(&UUID_B).unwrap());
ea.add_ava("memberof", Value::new_refer_s(&UUID_A).unwrap());
eb.add_ava("member", Value::new_refer_s(&UUID_C).unwrap());
eb.add_ava("memberof", Value::new_refer_s(&UUID_C).unwrap());
eb.add_ava("memberof", Value::new_refer_s(&UUID_B).unwrap());
eb.add_ava("memberof", Value::new_refer_s(&UUID_A).unwrap());
ec.add_ava("member", Value::new_refer_s(&UUID_A).unwrap());
ec.add_ava("memberof", Value::new_refer_s(&UUID_C).unwrap());
ec.add_ava("memberof", Value::new_refer_s(&UUID_B).unwrap());
ec.add_ava("memberof", Value::new_refer_s(&UUID_A).unwrap());
let preload = vec![ea, eb, ec];
run_modify_test!(
Ok(()),
preload,
filter!(f_eq("uuid", PartialValue::new_uuids(&UUID_C).unwrap())),
ModifyList::new_list(vec![Modify::Removed(
"member".to_string(),
PartialValue::new_refer_s(&UUID_A).unwrap()
)]),
None,
|au: &mut AuditScope, qs: &QueryServerWriteTransaction| {
assert_not_memberof!(au, qs, UUID_A, UUID_A);
assert_not_memberof!(au, qs, UUID_A, UUID_B);
assert_not_memberof!(au, qs, UUID_A, UUID_C);
assert_memberof!(au, qs, UUID_B, UUID_A);
assert_not_memberof!(au, qs, UUID_B, UUID_B);
assert_not_memberof!(au, qs, UUID_B, UUID_C);
assert_memberof!(au, qs, UUID_C, UUID_A);
assert_memberof!(au, qs, UUID_C, UUID_B);
assert_not_memberof!(au, qs, UUID_C, UUID_C);
assert_not_dirmemberof!(au, qs, UUID_A, UUID_A);
assert_not_dirmemberof!(au, qs, UUID_A, UUID_B);
assert_not_dirmemberof!(au, qs, UUID_A, UUID_C);
assert_dirmemberof!(au, qs, UUID_B, UUID_A);
assert_not_dirmemberof!(au, qs, UUID_B, UUID_B);
assert_not_dirmemberof!(au, qs, UUID_B, UUID_C);
assert_not_dirmemberof!(au, qs, UUID_C, UUID_A);
assert_dirmemberof!(au, qs, UUID_C, UUID_B);
assert_not_dirmemberof!(au, qs, UUID_C, UUID_C);
}
);
}
#[test]
fn test_modify_mo_del_multi_cycle() {
let mut ea: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EA);
let mut eb: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EB);
let mut ec: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EC);
let mut ed: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(ED);
ea.add_ava("member", Value::new_refer_s(&UUID_B).unwrap());
ea.add_ava("memberof", Value::new_refer_s(&UUID_D).unwrap());
ea.add_ava("memberof", Value::new_refer_s(&UUID_C).unwrap());
ea.add_ava("memberof", Value::new_refer_s(&UUID_B).unwrap());
ea.add_ava("memberof", Value::new_refer_s(&UUID_A).unwrap());
eb.add_ava("member", Value::new_refer_s(&UUID_C).unwrap());
eb.add_ava("memberof", Value::new_refer_s(&UUID_D).unwrap());
eb.add_ava("memberof", Value::new_refer_s(&UUID_C).unwrap());
eb.add_ava("memberof", Value::new_refer_s(&UUID_B).unwrap());
eb.add_ava("memberof", Value::new_refer_s(&UUID_A).unwrap());
ec.add_ava("member", Value::new_refer_s(&UUID_A).unwrap());
ec.add_ava("member", Value::new_refer_s(&UUID_D).unwrap());
ec.add_ava("memberof", Value::new_refer_s(&UUID_D).unwrap());
ec.add_ava("memberof", Value::new_refer_s(&UUID_C).unwrap());
ec.add_ava("memberof", Value::new_refer_s(&UUID_B).unwrap());
ec.add_ava("memberof", Value::new_refer_s(&UUID_A).unwrap());
ed.add_ava("member", Value::new_refer_s(&UUID_A).unwrap());
ed.add_ava("memberof", Value::new_refer_s(&UUID_D).unwrap());
ed.add_ava("memberof", Value::new_refer_s(&UUID_C).unwrap());
ed.add_ava("memberof", Value::new_refer_s(&UUID_B).unwrap());
ed.add_ava("memberof", Value::new_refer_s(&UUID_A).unwrap());
let preload = vec![ea, eb, ec, ed];
run_modify_test!(
Ok(()),
preload,
filter!(f_eq("uuid", PartialValue::new_uuids(&UUID_C).unwrap())),
ModifyList::new_list(vec![
Modify::Removed(
"member".to_string(),
PartialValue::new_refer_s(&UUID_A).unwrap()
),
Modify::Removed(
"member".to_string(),
PartialValue::new_refer_s(&UUID_D).unwrap()
),
]),
None,
|au: &mut AuditScope, qs: &QueryServerWriteTransaction| {
assert_not_memberof!(au, qs, UUID_A, UUID_A);
assert_not_memberof!(au, qs, UUID_A, UUID_B);
assert_not_memberof!(au, qs, UUID_A, UUID_C);
assert_memberof!(au, qs, UUID_A, UUID_D);
assert_memberof!(au, qs, UUID_B, UUID_A);
assert_not_memberof!(au, qs, UUID_B, UUID_B);
assert_not_memberof!(au, qs, UUID_B, UUID_C);
assert_memberof!(au, qs, UUID_B, UUID_D);
assert_memberof!(au, qs, UUID_C, UUID_A);
assert_memberof!(au, qs, UUID_C, UUID_B);
assert_not_memberof!(au, qs, UUID_C, UUID_C);
assert_memberof!(au, qs, UUID_C, UUID_D);
assert_not_memberof!(au, qs, UUID_D, UUID_A);
assert_not_memberof!(au, qs, UUID_D, UUID_B);
assert_not_memberof!(au, qs, UUID_D, UUID_C);
assert_not_memberof!(au, qs, UUID_D, UUID_D);
assert_not_dirmemberof!(au, qs, UUID_A, UUID_A);
assert_not_dirmemberof!(au, qs, UUID_A, UUID_B);
assert_not_dirmemberof!(au, qs, UUID_A, UUID_C);
assert_dirmemberof!(au, qs, UUID_A, UUID_D);
assert_dirmemberof!(au, qs, UUID_B, UUID_A);
assert_not_dirmemberof!(au, qs, UUID_B, UUID_B);
assert_not_dirmemberof!(au, qs, UUID_B, UUID_C);
assert_not_dirmemberof!(au, qs, UUID_B, UUID_D);
assert_not_dirmemberof!(au, qs, UUID_C, UUID_A);
assert_dirmemberof!(au, qs, UUID_C, UUID_B);
assert_not_dirmemberof!(au, qs, UUID_C, UUID_C);
assert_not_dirmemberof!(au, qs, UUID_C, UUID_D);
assert_not_dirmemberof!(au, qs, UUID_D, UUID_A);
assert_not_dirmemberof!(au, qs, UUID_D, UUID_B);
assert_not_dirmemberof!(au, qs, UUID_D, UUID_C);
assert_not_dirmemberof!(au, qs, UUID_D, UUID_D);
}
);
}
#[test]
fn test_delete_mo_simple() {
let mut ea: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EA);
let mut eb: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EB);
ea.add_ava("member", Value::new_refer_s(&UUID_B).unwrap());
eb.add_ava("memberof", Value::new_refer_s(&UUID_A).unwrap());
let preload = vec![ea, eb];
run_delete_test!(
Ok(()),
preload,
filter!(f_eq("uuid", PartialValue::new_uuids(&UUID_A).unwrap())),
None,
|au: &mut AuditScope, qs: &QueryServerWriteTransaction| {
assert_not_memberof!(au, qs, UUID_B, UUID_A);
assert_not_memberof!(au, qs, UUID_A, UUID_B);
assert_not_dirmemberof!(au, qs, UUID_B, UUID_A);
assert_not_dirmemberof!(au, qs, UUID_A, UUID_B);
}
);
}
#[test]
fn test_delete_mo_nested_head() {
let mut ea: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EA);
let mut eb: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EB);
let mut ec: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EC);
ea.add_ava("member", Value::new_refer_s(&UUID_B).unwrap());
eb.add_ava("memberof", Value::new_refer_s(&UUID_A).unwrap());
eb.add_ava("member", Value::new_refer_s(&UUID_C).unwrap());
ec.add_ava("memberof", Value::new_refer_s(&UUID_A).unwrap());
ec.add_ava("memberof", Value::new_refer_s(&UUID_B).unwrap());
let preload = vec![ea, eb, ec];
run_delete_test!(
Ok(()),
preload,
filter!(f_eq("uuid", PartialValue::new_uuids(&UUID_A).unwrap())),
None,
|au: &mut AuditScope, qs: &QueryServerWriteTransaction| {
assert_not_memberof!(au, qs, UUID_B, UUID_A);
assert_not_memberof!(au, qs, UUID_B, UUID_B);
assert_not_memberof!(au, qs, UUID_B, UUID_C);
assert_not_memberof!(au, qs, UUID_C, UUID_A);
assert_memberof!(au, qs, UUID_C, UUID_B);
assert_not_memberof!(au, qs, UUID_C, UUID_C);
assert_not_dirmemberof!(au, qs, UUID_B, UUID_A);
assert_not_dirmemberof!(au, qs, UUID_B, UUID_B);
assert_not_dirmemberof!(au, qs, UUID_B, UUID_C);
assert_not_dirmemberof!(au, qs, UUID_C, UUID_A);
assert_dirmemberof!(au, qs, UUID_C, UUID_B);
assert_not_dirmemberof!(au, qs, UUID_C, UUID_C);
}
);
}
#[test]
fn test_delete_mo_nested_branch() {
let mut ea: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EA);
let mut eb: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EB);
let mut ec: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EC);
ea.add_ava("member", Value::new_refer_s(&UUID_B).unwrap());
eb.add_ava("memberof", Value::new_refer_s(&UUID_A).unwrap());
eb.add_ava("member", Value::new_refer_s(&UUID_C).unwrap());
ec.add_ava("memberof", Value::new_refer_s(&UUID_A).unwrap());
ec.add_ava("memberof", Value::new_refer_s(&UUID_B).unwrap());
let preload = vec![ea, eb, ec];
run_delete_test!(
Ok(()),
preload,
filter!(f_eq("uuid", PartialValue::new_uuids(&UUID_B).unwrap())),
None,
|au: &mut AuditScope, qs: &QueryServerWriteTransaction| {
assert_not_memberof!(au, qs, UUID_A, UUID_A);
assert_not_memberof!(au, qs, UUID_A, UUID_B);
assert_not_memberof!(au, qs, UUID_A, UUID_C);
assert_not_memberof!(au, qs, UUID_C, UUID_A);
assert_not_memberof!(au, qs, UUID_C, UUID_B);
assert_not_memberof!(au, qs, UUID_C, UUID_C);
assert_not_dirmemberof!(au, qs, UUID_A, UUID_A);
assert_not_dirmemberof!(au, qs, UUID_A, UUID_B);
assert_not_dirmemberof!(au, qs, UUID_A, UUID_C);
assert_not_dirmemberof!(au, qs, UUID_C, UUID_A);
assert_not_dirmemberof!(au, qs, UUID_C, UUID_B);
assert_not_dirmemberof!(au, qs, UUID_C, UUID_C);
}
);
}
#[test]
fn test_delete_mo_cycle() {
let mut ea: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EA);
let mut eb: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EB);
let mut ec: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EC);
ea.add_ava("member", Value::new_refer_s(&UUID_B).unwrap());
ea.add_ava("memberof", Value::new_refer_s(&UUID_A).unwrap());
ea.add_ava("memberof", Value::new_refer_s(&UUID_B).unwrap());
ea.add_ava("memberof", Value::new_refer_s(&UUID_C).unwrap());
eb.add_ava("member", Value::new_refer_s(&UUID_C).unwrap());
eb.add_ava("memberof", Value::new_refer_s(&UUID_A).unwrap());
eb.add_ava("memberof", Value::new_refer_s(&UUID_B).unwrap());
eb.add_ava("memberof", Value::new_refer_s(&UUID_C).unwrap());
ec.add_ava("member", Value::new_refer_s(&UUID_A).unwrap());
ec.add_ava("memberof", Value::new_refer_s(&UUID_A).unwrap());
ec.add_ava("memberof", Value::new_refer_s(&UUID_B).unwrap());
ec.add_ava("memberof", Value::new_refer_s(&UUID_C).unwrap());
let preload = vec![ea, eb, ec];
run_delete_test!(
Ok(()),
preload,
filter!(f_eq("uuid", PartialValue::new_uuids(&UUID_A).unwrap())),
None,
|au: &mut AuditScope, qs: &QueryServerWriteTransaction| {
assert_not_memberof!(au, qs, UUID_B, UUID_A);
assert_not_memberof!(au, qs, UUID_B, UUID_B);
assert_not_memberof!(au, qs, UUID_B, UUID_C);
assert_not_memberof!(au, qs, UUID_C, UUID_A);
assert_memberof!(au, qs, UUID_C, UUID_B);
assert_not_memberof!(au, qs, UUID_C, UUID_C);
assert_not_dirmemberof!(au, qs, UUID_B, UUID_A);
assert_not_dirmemberof!(au, qs, UUID_B, UUID_B);
assert_not_dirmemberof!(au, qs, UUID_B, UUID_C);
assert_not_dirmemberof!(au, qs, UUID_C, UUID_A);
assert_dirmemberof!(au, qs, UUID_C, UUID_B);
assert_not_dirmemberof!(au, qs, UUID_C, UUID_C);
}
);
}
#[test]
fn test_delete_mo_multi_cycle() {
let mut ea: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EA);
let mut eb: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EB);
let mut ec: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(EC);
let mut ed: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(ED);
ea.add_ava("member", Value::new_refer_s(&UUID_B).unwrap());
ea.add_ava("memberof", Value::new_refer_s(&UUID_A).unwrap());
ea.add_ava("memberof", Value::new_refer_s(&UUID_B).unwrap());
ea.add_ava("memberof", Value::new_refer_s(&UUID_C).unwrap());
ea.add_ava("memberof", Value::new_refer_s(&UUID_D).unwrap());
eb.add_ava("member", Value::new_refer_s(&UUID_C).unwrap());
eb.add_ava("memberof", Value::new_refer_s(&UUID_A).unwrap());
eb.add_ava("memberof", Value::new_refer_s(&UUID_B).unwrap());
eb.add_ava("memberof", Value::new_refer_s(&UUID_C).unwrap());
eb.add_ava("memberof", Value::new_refer_s(&UUID_D).unwrap());
ec.add_ava("member", Value::new_refer_s(&UUID_A).unwrap());
ec.add_ava("member", Value::new_refer_s(&UUID_D).unwrap());
ec.add_ava("memberof", Value::new_refer_s(&UUID_A).unwrap());
ec.add_ava("memberof", Value::new_refer_s(&UUID_B).unwrap());
ec.add_ava("memberof", Value::new_refer_s(&UUID_C).unwrap());
ec.add_ava("memberof", Value::new_refer_s(&UUID_D).unwrap());
ed.add_ava("member", Value::new_refer_s(&UUID_A).unwrap());
ed.add_ava("memberof", Value::new_refer_s(&UUID_A).unwrap());
ed.add_ava("memberof", Value::new_refer_s(&UUID_B).unwrap());
ed.add_ava("memberof", Value::new_refer_s(&UUID_C).unwrap());
ed.add_ava("memberof", Value::new_refer_s(&UUID_D).unwrap());
let preload = vec![ea, eb, ec, ed];
run_delete_test!(
Ok(()),
preload,
filter!(f_eq("uuid", PartialValue::new_uuids(&UUID_B).unwrap())),
None,
|au: &mut AuditScope, qs: &QueryServerWriteTransaction| {
assert_not_memberof!(au, qs, UUID_A, UUID_B);
assert_not_memberof!(au, qs, UUID_A, UUID_A);
assert_memberof!(au, qs, UUID_A, UUID_C);
assert_memberof!(au, qs, UUID_A, UUID_D);
assert_not_memberof!(au, qs, UUID_C, UUID_A);
assert_not_memberof!(au, qs, UUID_C, UUID_B);
assert_not_memberof!(au, qs, UUID_C, UUID_C);
assert_not_memberof!(au, qs, UUID_C, UUID_D);
assert_not_memberof!(au, qs, UUID_D, UUID_A);
assert_not_memberof!(au, qs, UUID_D, UUID_B);
assert_memberof!(au, qs, UUID_D, UUID_C);
assert_not_memberof!(au, qs, UUID_D, UUID_D);
assert_not_dirmemberof!(au, qs, UUID_A, UUID_A);
assert_not_dirmemberof!(au, qs, UUID_A, UUID_B);
assert_dirmemberof!(au, qs, UUID_A, UUID_C);
assert_dirmemberof!(au, qs, UUID_A, UUID_D);
assert_not_dirmemberof!(au, qs, UUID_C, UUID_A);
assert_not_dirmemberof!(au, qs, UUID_C, UUID_B);
assert_not_dirmemberof!(au, qs, UUID_C, UUID_C);
assert_not_dirmemberof!(au, qs, UUID_C, UUID_D);
assert_not_dirmemberof!(au, qs, UUID_D, UUID_A);
assert_not_dirmemberof!(au, qs, UUID_C, UUID_B);
assert_dirmemberof!(au, qs, UUID_D, UUID_C);
assert_not_dirmemberof!(au, qs, UUID_D, UUID_D);
}
);
}
}