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
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
|
|
}
|
|
}
|
|
}
|