parent
ca989eb51d
commit
1f3e9b22d0
@ -0,0 +1,122 @@
|
|||||||
|
//! Data structures for the in-memory map.
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
};
|
||||||
|
use generational_arena::{
|
||||||
|
Arena, Index,
|
||||||
|
};
|
||||||
|
use cryptohelpers::{
|
||||||
|
rsa::{
|
||||||
|
RsaPublicKey, RsaPrivateKey,
|
||||||
|
Signature,
|
||||||
|
},
|
||||||
|
aes::AesKey,
|
||||||
|
sha256::Sha256Hash,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// An absolute (nested) index
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
|
pub struct AbsoluteIndex(Vec<Index>);
|
||||||
|
|
||||||
|
/// Possible value types of the data map
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub enum Data
|
||||||
|
{
|
||||||
|
Byte(u8),
|
||||||
|
Char(char),
|
||||||
|
Bool(bool),
|
||||||
|
|
||||||
|
/// Signed integer
|
||||||
|
SI(i64),
|
||||||
|
/// Unsigned integer
|
||||||
|
UI(u64),
|
||||||
|
/// Floating-point integer
|
||||||
|
FI(f64),
|
||||||
|
|
||||||
|
/// A UTF-8 text string
|
||||||
|
Text(String),
|
||||||
|
/// A binary blob
|
||||||
|
Binary(Vec<u8>),
|
||||||
|
|
||||||
|
/// A reference index to an item within the same `Datamap` as this one.
|
||||||
|
RelativeRef(Index),
|
||||||
|
/// A reference to an item N deep within nested `Map` elements.
|
||||||
|
///
|
||||||
|
/// The first `Index` specifies the `Map` data item at the root `Datamap` that contains the next, et cetera. The pointed to value is the last index.
|
||||||
|
AbsoluteRef(AbsoluteIndex),
|
||||||
|
|
||||||
|
/// A list of atoms
|
||||||
|
List(Vec<Atom>),
|
||||||
|
/// Another datamap
|
||||||
|
Map(Datamap),
|
||||||
|
|
||||||
|
/// An AES key
|
||||||
|
AesKey(AesKey),
|
||||||
|
/// An RSA keypair
|
||||||
|
RsaKeypair(RsaPrivateKey, RsaPublicKey),
|
||||||
|
/// An RSA private key
|
||||||
|
RsaPrivate(RsaPrivateKey),
|
||||||
|
/// An RSA public key
|
||||||
|
RsaPublic(RsaPublicKey),
|
||||||
|
|
||||||
|
/// A SHA256 hash
|
||||||
|
Hash(Sha256Hash),
|
||||||
|
|
||||||
|
/// A unique ID
|
||||||
|
Uuid(uuid::Uuid),
|
||||||
|
|
||||||
|
/// An RSA signature
|
||||||
|
Signature(Signature),
|
||||||
|
|
||||||
|
/// Nothing
|
||||||
|
Null,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An entry that may or may not be encrypted
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub enum MaybeEncrypted
|
||||||
|
{
|
||||||
|
Encrypted(Vec<u8>),
|
||||||
|
Unencrypted(Data),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Information about a map entry.
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Info
|
||||||
|
{
|
||||||
|
alt_name: Option<(String, String)>,
|
||||||
|
|
||||||
|
created: u64,
|
||||||
|
modified: u64,
|
||||||
|
|
||||||
|
enable_versioning: bool,
|
||||||
|
hidden: bool,
|
||||||
|
|
||||||
|
owner: Option<Vec<user::EntityID>>, //starts as the user that created (i.e. same as `created_by`), or `None` if ownership is disabled
|
||||||
|
signed: Option<Vec<Signature>>,
|
||||||
|
|
||||||
|
perms: user::Permissions,
|
||||||
|
|
||||||
|
created_by: user::UserID,
|
||||||
|
log: Vec<event::Event>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The root data containing map
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Datamap
|
||||||
|
{
|
||||||
|
data: Arena<Atom>,
|
||||||
|
ident: HashMap<Identifier, Index>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A value in a datamap, contains the information about the value and the value itself.
|
||||||
|
///
|
||||||
|
/// May also contain previous versions of this atom
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Atom(MaybeEncrypted, Info, Vec<Atom>);
|
||||||
|
|
||||||
|
/// An identifier for an item in a `Datamap`, or an item nested within many `Datamap`s.
|
||||||
|
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
||||||
|
pub struct Identifier(String);
|
@ -0,0 +1,28 @@
|
|||||||
|
//! When events happen to datamaps
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// What happened in the event
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub enum EventKind
|
||||||
|
{
|
||||||
|
Created,
|
||||||
|
Modified,
|
||||||
|
Deleted,
|
||||||
|
|
||||||
|
Renamed(String), // Previous name
|
||||||
|
Cloned(data::AbsoluteIndex), // Index of the new item
|
||||||
|
Moved,
|
||||||
|
|
||||||
|
Comment(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An event that happened to a data entry
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Event
|
||||||
|
{
|
||||||
|
who: Option<user::UserID>,
|
||||||
|
what: EventKind,
|
||||||
|
when: u64,
|
||||||
|
|
||||||
|
signed: Option<cryptohelpers::rsa::Signature>,
|
||||||
|
}
|
@ -1,69 +1,9 @@
|
|||||||
//! Server state
|
//! Server state
|
||||||
use super::*;
|
use super::*;
|
||||||
use std::{
|
|
||||||
collections::HashMap,
|
|
||||||
};
|
|
||||||
use generational_arena::{
|
|
||||||
Arena, Index,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Possible value types of the data map
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub enum Data
|
|
||||||
{
|
|
||||||
Byte(u8),
|
|
||||||
Char(char),
|
|
||||||
Bool(bool),
|
|
||||||
|
|
||||||
SI(i64),
|
|
||||||
UI(u64),
|
|
||||||
FI(f64),
|
|
||||||
|
|
||||||
Text(String),
|
|
||||||
Binary(Vec<u8>),
|
|
||||||
|
|
||||||
/// A reference index to an item within the same `Datamap` as this one.
|
|
||||||
RelativeRef(Index),
|
|
||||||
/// A reference to an item N deep within nested `Map` elements.
|
|
||||||
///
|
|
||||||
/// The first `Index` specifies the `Map` data item at the root `Datamap` that contains the next, et cetera. The pointed to value is the last index.
|
|
||||||
AbsoluteRef(Vec<Index>),
|
|
||||||
|
|
||||||
List(Vec<Atom>),
|
|
||||||
Map(Datamap),
|
|
||||||
|
|
||||||
Null,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Information about a map entry.
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct Info
|
|
||||||
{
|
|
||||||
created: u64,
|
|
||||||
modified: u64,
|
|
||||||
|
|
||||||
//TODO: Info about who owns it, perms, etc.
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The root data containing map
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct Datamap
|
|
||||||
{
|
|
||||||
data: Arena<Atom>,
|
|
||||||
ident: HashMap<Identifier, Index>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A value in a datamap, contains the information about the value and the value itself.
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct Atom(Data, Info);
|
|
||||||
|
|
||||||
/// An identifier for an item in a `Datamap`, or an item nested within many `Datamap`s.
|
|
||||||
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
|
||||||
pub struct Identifier(String);
|
|
||||||
|
|
||||||
/// Contains the state of the whole program
|
/// Contains the state of the whole program
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ServerState
|
pub struct ServerState
|
||||||
{
|
{
|
||||||
root: Datamap,
|
root: data::Datamap,
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,276 @@
|
|||||||
|
//! 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>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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: u32 {
|
||||||
|
/// 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()
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in new issue