You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
292 lines
6.5 KiB
292 lines
6.5 KiB
//! 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<String>,
|
|
comment: Option<String>,
|
|
|
|
/// Corresponds to a Unix `superuser`.
|
|
///
|
|
/// All permissions checks are bypassed for a user with this set.
|
|
is_god: bool,
|
|
|
|
groups: Vec<GroupID>,
|
|
}
|
|
|
|
/// 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<String>,
|
|
comment: Option<String>,
|
|
|
|
inherits: Option<Vec<GroupID>>,
|
|
}
|
|
|
|
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<Item = GroupID> +'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<EntityID, Permission>,
|
|
}
|
|
|
|
/// 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<UserID, User>,
|
|
groups: HashMap<GroupID, Group>,
|
|
}
|
|
|
|
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()
|
|
}
|
|
}
|