//! Information about users, perms, etc use super::*; use std::collections::HashMap; use std::borrow::Cow; use std::ops::Deref; use bitflags::bitflags; id_type!(pub UserID: "A unique user ID"); id_type!(pub GroupID: "A user group ID"); #[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 reference to a user in a userspace from their ID. #[derive(Debug, Clone, PartialEq, Eq)] pub struct UserRef<'a>(UserID, &'a Userspace); impl<'a> UserRef<'a> { /// The ID of this user reference #[inline] pub fn id(&self) -> &UserID { &self.0 } /// The userspace of this user #[inline] pub fn space(&self) -> &Userspace { self.1 } /// # Note /// This is for testing purposes only, and should never return `None`. #[inline(always)] fn try_user(&self) -> Option<&'a User> { self.1.users.get(&self.0) } /// The user this ID is referring to #[inline] pub fn user(&self) -> &User { self.try_user().unwrap() } /// Consume into a reference to the user with the lifetime of the `Userspace` containing it. #[inline] pub fn into_user(self) -> &'a User { self.try_user().unwrap() } } impl<'a> Deref for UserRef<'a> { type Target = User; fn deref(&self) -> &Self::Target { self.user() } } /// A reference to a group in a userspace from their group ID. #[derive(Debug, Clone, PartialEq, Eq)] pub struct GroupRef<'a>(GroupID, &'a Userspace); impl<'a> GroupRef<'a> { /// The ID of this group reference #[inline] pub fn id(&self) -> &GroupID { &self.0 } /// The userspace of this group ref #[inline] pub fn space(&self) -> &Userspace { self.1 } /// # Note /// This is for testing purposes only, and should never return `None`. #[inline(always)] fn try_group(&self) -> Option<&'a Group> { self.1.groups.get(&self.0) } /// The group this ID is referring to #[inline] pub fn group(&self) -> &Group { self.try_group().unwrap() } /// Consume into a reference to the group with the lifetime of the `Userspace` containing it. #[inline] pub fn into_group(self) -> &'a Group { self.try_group().unwrap() } } impl<'a> Deref for GroupRef<'a> { type Target = Group; fn deref(&self) -> &Self::Target { self.group() } } /// 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>, } pub trait AsEntityId { fn entity_id(&self) -> Cow<'_, EntityID>; } assert_object_safe!(AsEntityId); 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]; } assert_object_safe!(Entity); 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; } } impl Default for Permission { #[inline(always)] fn default() -> Self { Self::READ } } /// A set of permissions informations for users and/or groups #[derive(Debug, Clone, PartialEq, Eq, 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() } /// Get a user reference from their ID pub fn user_ref(&self, id: UserID) -> Option> { if self.users.contains_key(&id) { Some(UserRef(id, &self)) } else { None } } /// Get a group reference from their ID pub fn group_ref(&self, id: GroupID) -> Option> { if self.groups.contains_key(&id) { Some(GroupRef(id, &self)) } else { None } } }