From 1f3e9b22d0f7eb7871c5eecb28f58c0b18c09623 Mon Sep 17 00:00:00 2001 From: Avril Date: Wed, 18 Nov 2020 21:37:12 +0000 Subject: [PATCH] adding user --- Cargo.lock | 63 +++++++--- Cargo.toml | 7 +- src/server/data.rs | 122 ++++++++++++++++++++ src/server/event.rs | 28 +++++ src/server/mod.rs | 4 + src/server/state.rs | 62 +--------- src/server/user.rs | 276 ++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 480 insertions(+), 82 deletions(-) create mode 100644 src/server/data.rs create mode 100644 src/server/event.rs create mode 100644 src/server/user.rs diff --git a/Cargo.lock b/Cargo.lock index aa97598..d5919b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -119,12 +119,6 @@ dependencies = [ "safemem", ] -[[package]] -name = "build_const" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39" - [[package]] name = "byte-tools" version = "0.3.1" @@ -161,6 +155,19 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time", + "winapi 0.3.9", +] + [[package]] name = "cloudabi" version = "0.0.3" @@ -189,15 +196,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" -[[package]] -name = "crc" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb" -dependencies = [ - "build_const", -] - [[package]] name = "crypto-mac" version = "0.9.1" @@ -210,11 +208,10 @@ dependencies = [ [[package]] name = "cryptohelpers" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cfc491baaffd7cbd6acc02ebd23564760d83a2c17e1a47e6a04a8d5a86e7fb5" +checksum = "31fca6629a75c37a8831f93133f86e20efb8dd7a94060e32635ebf9a62aca12f" dependencies = [ - "crc", "getrandom 0.1.15", "hex-literal", "hmac", @@ -224,7 +221,6 @@ dependencies = [ "serde", "serde_derive", "sha2", - "tokio", ] [[package]] @@ -232,6 +228,8 @@ name = "datse" version = "0.1.0" dependencies = [ "base64 0.13.0", + "bitflags", + "chrono", "color-eyre", "cryptohelpers", "futures", @@ -245,6 +243,7 @@ dependencies = [ "regex", "serde", "smallmap", + "stack-vec", "tokio", "uuid", "warp", @@ -890,6 +889,25 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg 1.0.1", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg 1.0.1", +] + [[package]] name = "num_cpus" version = "1.13.0" @@ -1428,6 +1446,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "735b09f6ab554c4165d045a7d67b3b7b5248acb39463dffba38ebced1b9110e2" dependencies = [ "rustc_version", + "serde", ] [[package]] @@ -1442,6 +1461,12 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "stack-vec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "969d02b2cf038972b7f2d285f246044771da9fb8cc26a9d6b129236b784d3bd6" + [[package]] name = "subtle" version = "2.3.0" diff --git a/Cargo.toml b/Cargo.toml index a811fe9..09aa88e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,8 +21,10 @@ client = [] [dependencies] base64 = "0.13.0" +bitflags = "1.2.1" +chrono = "0.4.19" color-eyre = {version = "0.5", default-features=false} -cryptohelpers = {version = "1.5.1", features= ["sha256", "rsa", "serde"]} +cryptohelpers = { version = "1.6", default-features=false, features = ["sha256", "rsa", "serialise", "aes"] } futures = "0.3.8" generational-arena = {version = "0.2.8", features= ["serde"]} getrandom = "0.2.0" @@ -33,7 +35,8 @@ pretty_env_logger = "0.4.0" rand = "0.7.3" regex = "1.4.2" serde = {version = "1.0", features = ["derive"]} -smallmap = "1.2" +smallmap = {version = "1.2", features= ["serde"]} +stack-vec = "0.1.0" tokio = {version = "0.2", features = ["full"]} uuid = {version = "0.8.1", features = ["v4","serde"]} warp = "0.2.5" diff --git a/src/server/data.rs b/src/server/data.rs new file mode 100644 index 0000000..63f0552 --- /dev/null +++ b/src/server/data.rs @@ -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); + +/// 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), + + /// 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), + /// 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), + 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>, //starts as the user that created (i.e. same as `created_by`), or `None` if ownership is disabled + signed: Option>, + + perms: user::Permissions, + + created_by: user::UserID, + log: Vec, +} + +/// The root data containing map +#[derive(Debug, Serialize, Deserialize)] +pub struct Datamap +{ + data: Arena, + ident: HashMap, +} + +/// 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); + +/// 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); diff --git a/src/server/event.rs b/src/server/event.rs new file mode 100644 index 0000000..9f58618 --- /dev/null +++ b/src/server/event.rs @@ -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, + what: EventKind, + when: u64, + + signed: Option, +} diff --git a/src/server/mod.rs b/src/server/mod.rs index 6674aa3..bdb8213 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -6,8 +6,12 @@ pub struct Config } +mod event; +mod user; +mod data; mod state; + #[cfg(feature="server-http")] pub mod web; #[cfg(feature="server-tcp")] pub mod tcp; diff --git a/src/server/state.rs b/src/server/state.rs index 51548fe..8b87f13 100644 --- a/src/server/state.rs +++ b/src/server/state.rs @@ -1,69 +1,9 @@ //! Server state 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), - - /// 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), - - List(Vec), - 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, - ident: HashMap, -} - -/// 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 #[derive(Debug)] pub struct ServerState { - root: Datamap, + root: data::Datamap, } diff --git a/src/server/user.rs b/src/server/user.rs new file mode 100644 index 0000000..5698817 --- /dev/null +++ b/src/server/user.rs @@ -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, + comment: Option, + + /// Corresponds to a Unix `superuser`. + /// + /// All permissions checks are bypassed for a user with this set. + is_god: bool, + + groups: Vec, +} + +/// 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>; +} + +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, +} + +/// 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() + } +}