From d3c644836abe317e570aea00f71f4abad53f9cc1 Mon Sep 17 00:00:00 2001 From: Avril Date: Fri, 20 Nov 2020 17:03:36 +0000 Subject: [PATCH] Group inheritance graph flattening iterator --- src/server/user.rs | 12 ++-- src/server/user/group.rs | 126 +++++++++++++++++++++++++++++++++++ src/server/user/userspace.rs | 113 ++++++++++++++++++++++++++++++- 3 files changed, 245 insertions(+), 6 deletions(-) create mode 100644 src/server/user/group.rs diff --git a/src/server/user.rs b/src/server/user.rs index 41c3dc2..f479d2e 100644 --- a/src/server/user.rs +++ b/src/server/user.rs @@ -29,6 +29,8 @@ pub struct User groups: Vec, } +//TODO: Iterator over all `Group`s a user is a part of (i.e. each group they're in's deduped & flattened inheritance graph) + /// A reference to a user in a userspace from their ID. #[derive(Debug, Clone, PartialEq, Eq)] pub struct UserRef<'a>(UserID, &'a Userspace); @@ -121,9 +123,6 @@ impl<'a> Deref for GroupRef<'a> } } - - - /// A group is a way of setting permissions for a whole set of users. /// /// Users have groups, not the other way around. @@ -147,6 +146,8 @@ pub struct Group inherits: Option>, } +submod!(group; "impls for `Group` related things"); + pub trait AsEntityId { fn entity_id(&self) -> Cow<'_, EntityID>; @@ -324,12 +325,13 @@ bitflags! { struct Permission: u16 { /// Cannot read or write the item. /// - /// However they can see it, unless it is marked "hidden". + /// However they can see it, unless it is marked with tag attr "hidden". const NONE = 0; - /// Can read from the item but cannot write to it + /// Can read from the item const READ = 1; /// Can write to the item /// + /// # Note /// This does not imply `READ`. const WRITE = 2; diff --git a/src/server/user/group.rs b/src/server/user/group.rs new file mode 100644 index 0000000..7000f0a --- /dev/null +++ b/src/server/user/group.rs @@ -0,0 +1,126 @@ +//! Group specific impls +use super::*; +use std::collections::{ + VecDeque, + HashSet, hash_set, +}; + +impl Group +{ + /// Is this group hidden from searches? + #[inline(always)] pub fn is_hidden(&self) -> bool + { + self.hidden + } + /// Set if this group should be hidden from searches. + #[inline(always)] pub fn set_hidden(&mut self, hide: bool) + { + self.hidden = hide; + } + + /// The flattened inheritance graph of this `Group` within this `Userspace`. + pub fn inherits_from<'g, 'u>(&'g self, space: &'u Userspace) -> GroupInheritanceIter<'u> + where 'g: 'u + { + GroupInheritanceIter { + group: self, + space, + + level: self.inherits.clone().unwrap_or_default().into(), + done: HashSet::new(), + + is_cyclic: false, + + cyclic_refs: Default::default(), + } + } +} + +/// An iterator over a `Group`'s entire inheritance graph. +#[derive(Debug, Clone)] +pub struct GroupInheritanceIter<'u> +{ + group: &'u Group, + space: &'u Userspace, + + level: VecDeque, + + done: HashSet, + + is_cyclic: bool, + + cyclic_refs: HashSet<&'u GroupID>, +} + +impl<'a> GroupInheritanceIter<'a> +{ + /// The group this iterator is working for + #[inline] pub fn base_group(&self) -> &Group + { + self.group + } + /// The userspace this iterator is searching in + #[inline] pub fn userspace(&self) -> &Userspace + { + self.space + } + + /// Does this inheritance graph contain cyclic references so far? + #[inline] pub fn contains_cyclic_references(&self) -> bool + { + self.is_cyclic + } + /// The cyclic references found by this iterator so far (if any). + #[inline] pub fn cyclic_refs(&self) -> hash_set::Iter<'_, &'a GroupID> + { + self.cyclic_refs.iter() + } + /// All group IDs that have been processed by the iterator so far + #[inline] pub fn processed_group_ids(&self) -> hash_set::Iter<'_, GroupID> + { + self.done.iter() + } +} + +impl<'u> Iterator for GroupInheritanceIter<'u> +{ + type Item = &'u Group; + fn next(&mut self) -> Option + { + match self.level + .pop_front() + .map(|id| (self.space.groups.get(&id), + self.done.insert(id))) + { + #[cold] Some((None, _)) => panic!("Group inheritance graph for group ID {:?} contained invalid ID not found within this userspace", self.group.id), + Some((Some(group), false)) => { + // We've already processed this ID, it is a cyclic reference. + // Ignore it and continue + if cfg!(debug_assertions) && !self.is_cyclic { + warn!("Cyclic reference found for group ID {:?} while calculating the inheritance graph of group {:?}", group.id, self.group.id); + } + self.is_cyclic = true; + self.cyclic_refs.insert(&group.id); + self.next() + }, + Some((Some(group), _)) => { + if let Some(high) = &group.inherits { + self.level.reserve(high.len()); + for high in high.iter().cloned().rev() { + self.level.push_front(high); + } + } + Some(group) + }, + _ => None, + } + } + fn size_hint(&self) -> (usize, Option) { + match self.level.len() { + 0 => (0, Some(0)), + n => (n, None), + } + } +} +impl<'a> std::iter::FusedIterator for GroupInheritanceIter<'a>{} + diff --git a/src/server/user/userspace.rs b/src/server/user/userspace.rs index 2f0b5d1..8864539 100644 --- a/src/server/user/userspace.rs +++ b/src/server/user/userspace.rs @@ -1,4 +1,6 @@ use super::*; +use std::collections::hash_map; +use std::iter; impl Userspace { @@ -27,6 +29,115 @@ impl Userspace None } } + + /// An iterator over the users in this instance + pub fn users(&self) -> UserIter<'_> + { + UserIter(self.users.iter()) + } + + /// A mutable iterator over the users in this instance + pub fn users_mut(&mut self) -> UserIterMut<'_> + { + UserIterMut(self.users.iter_mut()) + } + + /// An iterator over the groups in this instance + pub fn groups(&self) -> GroupIter<'_> + { + GroupIter(self.groups.iter()) + } + + /// A mutable iterator over the groups in this instance + pub fn groups_mut(&mut self) -> GroupIterMut<'_> + { + GroupIterMut(self.groups.iter_mut()) + } + + /// Number of users + pub fn users_len(&self) -> usize + { + self.users.len() + } + /// Number of groups + pub fn groups_len(&self) -> usize + { + self.groups.len() + } +} + +/// Iterator over users +#[derive(Debug, Clone)] +pub struct UserIter<'a>(hash_map::Iter<'a, UserID, User>); + +impl<'a> Iterator for UserIter<'a> +{ + type Item = &'a User; + fn next(&mut self) -> Option + { + self.0.next().map(|x| x.1) + } + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } } +impl<'a> iter::FusedIterator for UserIter<'a>{} +impl<'a> iter::ExactSizeIterator for UserIter<'a>{} -//TODO: `UserIter`, `GroupIter` + +/// Mutable iterator over users +#[derive(Debug)] +pub struct UserIterMut<'a>(hash_map::IterMut<'a, UserID, User>); + +impl<'a> Iterator for UserIterMut<'a> +{ + type Item = &'a mut User; + fn next(&mut self) -> Option + { + self.0.next().map(|x| x.1) + } + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } +} +impl<'a> iter::FusedIterator for UserIterMut<'a>{} +impl<'a> iter::ExactSizeIterator for UserIterMut<'a>{} + + +/// Iterator over groups in a `Userspace` +#[derive(Debug, Clone)] +pub struct GroupIter<'a>(hash_map::Iter<'a, GroupID, Group>); + +impl<'a> Iterator for GroupIter<'a> +{ + type Item = &'a Group; + fn next(&mut self) -> Option + { + self.0.next().map(|x| x.1) + } + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } +} +impl<'a> iter::FusedIterator for GroupIter<'a>{} +impl<'a> iter::ExactSizeIterator for GroupIter<'a>{} + + + +/// Mutable iterator over groups in a `Userspace` +#[derive(Debug)] +pub struct GroupIterMut<'a>(hash_map::IterMut<'a, GroupID, Group>); + +impl<'a> Iterator for GroupIterMut<'a> +{ + type Item = &'a mut Group; + fn next(&mut self) -> Option + { + self.0.next().map(|x| x.1) + } + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } +} +impl<'a> iter::FusedIterator for GroupIterMut<'a>{} +impl<'a> iter::ExactSizeIterator for GroupIterMut<'a>{}