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.

397 lines
8.4 KiB

//! 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<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 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<String>,
comment: Option<String>,
inherits: Option<Vec<GroupID>>,
}
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<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()
}
/// Get a user reference from their ID
pub fn user_ref(&self, id: UserID) -> Option<UserRef<'_>>
{
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<GroupRef<'_>>
{
if self.groups.contains_key(&id) {
Some(GroupRef(id, &self))
} else {
None
}
}
}