Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 20 additions & 4 deletions crates/lance-graph-dir-sim/src/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@
//! every live plane. Ordinals are valid only inside one view; the overlay
//! keeps identities wherever an ordinal could shift.

use crate::GroupWhere;
use crate::snapshot::{
bit, clear_bit, is_mail_recipient, is_owner, set_bit, Dicts, GroupOrdinal, Population,
Snapshot, UserOrdinal, MAX_GROUPS, MAX_USERS, NONE,
Dicts, GroupOrdinal, MAX_GROUPS, MAX_USERS, NONE, Population, Snapshot, UserOrdinal, bit,
clear_bit, is_mail_recipient, is_owner, set_bit,
};
use crate::GroupWhere;
use lance_graph_mask_risc::{words_for, Foreign, LaneRef, Planes};
use lance_graph_mask_risc::{Foreign, LaneRef, Planes, words_for};
use lance_graph_quack::{Cmp, Col, Filter, Mask};
use ogar_dir_core::{Dn128, Guid128};
use ogar_dir_sim::{
Expand Down Expand Up @@ -548,6 +548,22 @@ impl<'s> View<'s> {
self.members_walk(group, Some(&allowed))
}

/// [`Self::members_transitive`] through the groups in `within` only:
/// `group` itself and every nested group on the way must be in the set.
/// For a consumer whose own notion of a group is an explicit list (a CRM
/// team hierarchy, where a team's sub-teams are nested groups but an
/// unrelated security group nested in a team is not a sub-team). A
/// GUID in `within` that is not an existing group is ignored.
pub fn members_transitive_within(&self, group: &Guid128, within: &[Guid128]) -> Vec<Guid128> {
let mut allowed = vec![0u64; words_for(self.groups_len())];
for g in within {
if let Some(o) = self.group_ordinal(g) {
set_bit(&mut allowed, usize::from(o.0));
}
}
self.members_walk(group, Some(&allowed))
}

/// The groups that satisfy `w`, as a bitmap over group ordinals.
fn groups_bitmap(&self, w: &GroupWhere) -> Vec<u64> {
let mut bits = vec![0u64; words_for(self.groups_len())];
Expand Down
41 changes: 36 additions & 5 deletions crates/lance-graph-dir-sim/tests/nested_groups.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,11 +255,13 @@ fn empty_property_junctions_are_identities() {
assert_eq!(selected(&view, &GroupWhere::And(vec![])), all);
assert!(selected(&view, &GroupWhere::Or(vec![])).is_empty());
assert_eq!(selected(&view, &not(GroupWhere::Or(vec![]))), all);
assert!(selected(
&view,
&GroupWhere::And(vec![GroupWhere::Or(vec![]), is(GroupProperty::MailEnabled)])
)
.is_empty());
assert!(
selected(
&view,
&GroupWhere::And(vec![GroupWhere::Or(vec![]), is(GroupProperty::MailEnabled)])
)
.is_empty()
);
}

// Mail is chained addressing, never inherited: mail to S1 reaches its
Expand Down Expand Up @@ -387,3 +389,32 @@ fn ad_group_type_sets_the_flag() {
.collect();
assert_eq!(flags, vec![Some(true), Some(false), None]);
}

#[test]
fn members_transitive_within_walks_only_the_listed_groups() {
let mut st = VersionStore::new();
let v = st.observe("lab", 1, observed()).unwrap();
let view = st.view(v).unwrap();
// S1 alone: its direct user u1; S2, D1 and UNREAD are not walked.
assert_eq!(
view.members_transitive_within(&g(S1), &[g(S1)]),
vec![g(U1)]
);
// S1 through D1: u3, and S3 (not listed) is not walked, so u4 is not.
assert_eq!(
view.members_transitive_within(&g(S1), &[g(S1), g(D1)]),
vec![g(U1), g(U3)]
);
// Every group listed: the same as the unrestricted walk.
let all = [g(S1), g(S2), g(S3), g(D1), g(UNREAD)];
assert_eq!(
view.members_transitive_within(&g(S1), &all),
view.members_transitive(&g(S1))
);
// The start group not listed: nothing. A non-group GUID is ignored.
assert!(view.members_transitive_within(&g(S1), &[g(S2)]).is_empty());
assert_eq!(
view.members_transitive_within(&g(S1), &[g(S1), g(U2)]),
vec![g(U1)]
);
}
Loading