//! Information about users, perms, etc use super::*; use std::collections::HashMap; use std::borrow::Cow; use bitflags::bitflags; /// The ID type used for backing user/group IDs type GenericID = uuid::Uuid; /// A user group ID #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)] pub struct GroupID(GenericID); /// A unique user ID #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)] pub struct UserID(GenericID); #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct User { id: UserID, name: String, hidden: bool, desc: Option, comment: Option, /// Corresponds to a Unix `superuser`. /// /// All permissions checks are bypassed for a user with this set. is_god: bool, groups: Vec, } /// A group is a way of setting permissions for a whole set of users. /// /// Users have groups, not the other way around. /// A user can have multiple groups or none. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct Group { id: GroupID, hidden: bool, /// Corresponds to a Unix `superuser`. /// /// All permissions checks are bypassed for a user in a group with this set. is_god: bool, name: String, desc: Option, comment: Option, inherits: Option>, } impl Group { /* //TODO: Create bespoke iterator type for this, it's not possible here. pub fn inherits<'b, 'a>(&'b self, space: &'a Userspace) -> impl Iterator +'a where 'b: 'a { match &self.inherits { Some(inherits) => inherits.iter() .filter_map(|group| group.find_in_space(space).map(|parent| parent.inherits(space))).flatten(), None => todo!() } }*/ } pub trait AsEntityId { fn entity_id(&self) -> Cow<'_, EntityID>; } impl AsEntityId for EntityID { #[inline(always)] fn entity_id(&self) -> Cow<'_, EntityID> { Cow::Borrowed(self) } } impl AsEntityId for UserID { fn entity_id(&self) -> Cow<'_, EntityID> { Cow::Owned(EntityID::User(self.clone())) } } impl AsEntityId for GroupID { fn entity_id(&self) -> Cow<'_, EntityID> { Cow::Owned(EntityID::Group(self.clone())) } } impl<'a, T> AsEntityId for &'a T where T: AsEntityId { #[inline(always)] fn entity_id(&self) -> Cow<'_, EntityID> { T::entity_id(self) } } /// A trait for items that can have permissions bits set for them. /// Users and groups implement this. pub trait Entity { fn generic_id(&self) -> &GenericID; fn name(&self) -> &str; fn desc(&self) -> Option<&str>; fn comment(&self) -> Option<&str>; fn superuser(&self) -> bool; /// # Note /// This does not flatten inherited groups, that needs to be performed later fn groups(&self) -> &[GroupID]; } impl Entity for User { #[inline] fn generic_id(&self) -> &GenericID { &self.id.0 } #[inline] fn name(&self) -> &str { &self.name[..] } #[inline] fn desc(&self) -> Option<&str> { self.desc.as_ref().map(String::as_str) } #[inline] fn comment(&self) -> Option<&str> { self.comment.as_ref().map(String::as_str) } #[inline] fn superuser(&self) -> bool { self.is_god } /// # Note /// This does not flatten inherited groups, that needs to be performed later #[inline] fn groups(&self) -> &[GroupID] { &self.groups[..] } } impl Entity for Group { #[inline] fn generic_id(&self) -> &GenericID { &self.id.0 } #[inline] fn name(&self) -> &str { &self.name[..] } #[inline] fn desc(&self) -> Option<&str> { self.desc.as_ref().map(String::as_str) } #[inline] fn comment(&self) -> Option<&str> { self.comment.as_ref().map(String::as_str) } #[inline] fn superuser(&self) -> bool { self.is_god } /// # Note /// This does not flatten inherited groups, that needs to be performed later #[inline] fn groups(&self) -> &[GroupID] { self.inherits.as_ref().map(Vec::as_slice).unwrap_or(&[]) } } /// Either a single user or a group of users #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)] pub enum EntityID { User(UserID), Group(GroupID), } impl UserID { /// Attempt to find this user in this userspace pub fn find_in_space<'a>(&self, space: &'a Userspace) -> Option<&'a User> { space.users.get(self) } /// Attempt to find a mutable reference to this user in this userspace pub fn find_in_space_mut<'a>(&self, space: &'a mut Userspace) -> Option<&'a mut User> { space.users.get_mut(self) } } impl GroupID { /// Attempt to find this group in this userspace pub fn find_in_space<'a>(&self, space: &'a Userspace) -> Option<&'a Group> { space.groups.get(self) } /// Attempt to find a mutable reference to this group in this userspace pub fn find_in_space_mut<'a>(&self, space: &'a mut Userspace) -> Option<&'a mut Group> { space.groups.get_mut(self) } } impl EntityID { /// Attempt to find the entity that implements this ID in this space pub fn find_in_space<'a>(&self, space: &'a Userspace) -> Option<&'a (dyn Entity + 'static)> { match self { Self::User(id) => space.users.get(id).map(|x| x as &dyn Entity), Self::Group(id) => space.groups.get(id).map(|x| x as &dyn Entity), } } } bitflags! { /// A permission a user or group has for a data item. #[derive(Serialize, Deserialize)] struct Permission: u16 { /// Cannot read or write the item. /// /// However they can see it, unless it is marked "hidden". const NONE = 0; /// Can read from the item but cannot write to it const READ = 1; /// Can write to the item /// /// This does not imply `READ`. const WRITE = 2; /// Full access const FULL = Self::READ.bits | Self::WRITE.bits; } } /// A set of permissions informations for users and/or groups #[derive(Debug, Serialize, Deserialize)] pub struct Permissions { blanket: Permission, spec: smallmap::Map, } /// Contains all users and groups #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct Userspace { // use maps here for easier lookup, despite duplicating ID data users: HashMap, groups: HashMap, } impl Userspace { /// Is this `User` or `Group` ID present in this space? pub fn contains_id(&self, ent: &impl AsEntityId) -> bool { ent.entity_id().find_in_space(self).is_some() } }