Lots of changes
Notable ones are that now every message has a field for the target server, and that messages have an "optional" ID in that they dont have it from client -> server and they do have it from server -> client.
This commit is contained in:
+88
-26
@@ -1,7 +1,7 @@
|
||||
use uuid::Uuid;
|
||||
use crate::message::{MessageId,TaggedMessage,VerificationError,Relevance};
|
||||
use crate::state::{StateType,StateValue,StatePath,StatePermission,StatePermissionKey};
|
||||
use crate::{RoomId,Group,Role,User,GroupPower};
|
||||
use crate::state::{self,StateType,StateValue,StatePath,StatePermission,StatePermissionKey};
|
||||
use crate::{RoomId,Group,Role,User,GroupPower,ServerAddr};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use time::OffsetDateTime;
|
||||
|
||||
@@ -13,19 +13,19 @@ use time::OffsetDateTime;
|
||||
|
||||
|
||||
|
||||
#[derive(Serialize,Deserialize,Clone,Debug)]
|
||||
/// This exists solely to allow the DB to finish tagging the message. It is every field of a
|
||||
/// message except for the ID which can be created using a Postgres serial type
|
||||
pub struct InsertableClientMessage {
|
||||
pub message: ClientMessage,
|
||||
#[serde(with="time::serde::timestamp")]
|
||||
pub client_timestamp: OffsetDateTime,
|
||||
#[serde(with="time::serde::timestamp")]
|
||||
pub server_timestamp: OffsetDateTime,
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub signature: Box<[u8]>,
|
||||
pub user: User
|
||||
}
|
||||
//#[derive(Serialize,Deserialize,Clone,Debug)]
|
||||
///// This exists solely to allow the DB to finish tagging the message. It is every field of a
|
||||
///// message except for the ID which can be created using a Postgres serial type
|
||||
//pub struct InsertableClientMessage {
|
||||
// pub message: ClientMessage,
|
||||
// #[serde(with="time::serde::timestamp")]
|
||||
// pub client_timestamp: OffsetDateTime,
|
||||
// #[serde(with="time::serde::timestamp")]
|
||||
// pub server_timestamp: OffsetDateTime,
|
||||
// #[serde(with = "serde_bytes")]
|
||||
// pub signature: Box<[u8]>,
|
||||
// pub user: User
|
||||
//}
|
||||
|
||||
|
||||
#[derive(Serialize,Deserialize,Clone,Debug)]
|
||||
@@ -39,17 +39,26 @@ pub struct SignedClientMessage {
|
||||
// Can I reserialize the message and check the signature that way??
|
||||
// That would be very brittle but there isn't a good way to extract the
|
||||
// bytes using serde. I'll write something better at some point
|
||||
//
|
||||
// Should this be optional?
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub signature: Box<[u8]>
|
||||
pub signature: Box<[u8]>,
|
||||
|
||||
// Which server should process the request. Most messages are forwardable but some are not,
|
||||
// like auth requests and media uploads.
|
||||
//
|
||||
// Setting this to None makes it a local message
|
||||
pub target: Option<ServerAddr>
|
||||
}
|
||||
impl SignedClientMessage {
|
||||
pub fn tag(self, username: User) -> InsertableClientMessage {
|
||||
InsertableClientMessage {
|
||||
pub fn tag(self, username: User, servername: ServerAddr) -> TaggedMessage {
|
||||
TaggedMessage {
|
||||
message: self.message,
|
||||
client_timestamp: self.timestamp,
|
||||
server_timestamp: OffsetDateTime::now_utc(),
|
||||
signature: self.signature,
|
||||
user: username,
|
||||
target: self.target.unwrap_or(servername)
|
||||
}
|
||||
}
|
||||
pub fn verify(&self) -> Result<bool,VerificationError> {
|
||||
@@ -59,7 +68,6 @@ impl SignedClientMessage {
|
||||
|
||||
#[derive(Serialize,Deserialize,Clone,Debug)]
|
||||
pub enum ClientMessage {
|
||||
|
||||
Auth{
|
||||
username: String,
|
||||
password: String
|
||||
@@ -79,7 +87,13 @@ pub enum ClientMessage {
|
||||
// Should it be one message type or mutiple?
|
||||
Message {
|
||||
body: String,
|
||||
room_id: RoomId
|
||||
room_id: RoomId,
|
||||
// Can I overload this to have an optional ID? And reject any
|
||||
// incoming messages that specify an ID? How else do I return an ID
|
||||
// only for this message
|
||||
//
|
||||
// This is a hacky way to do it but idk how else to
|
||||
id: Option<MessageId>
|
||||
},
|
||||
// Private message/invite mechanism
|
||||
MessagePost {
|
||||
@@ -100,8 +114,8 @@ pub enum ClientMessage {
|
||||
StateCreate {
|
||||
room_id: RoomId,
|
||||
path: StatePath,
|
||||
ty: StateType,
|
||||
permissions: Option<Vec<StatePermission>>
|
||||
content: StateValue,
|
||||
permissions: Option<state::PermissionTable>
|
||||
},
|
||||
StateWrite {
|
||||
room_id: RoomId,
|
||||
@@ -127,9 +141,17 @@ pub enum ClientMessage {
|
||||
path: StatePath
|
||||
},
|
||||
PermissionAdd {
|
||||
room_id: RoomId,
|
||||
path: StatePath,
|
||||
permission: StatePermission
|
||||
},
|
||||
PermissionRead {
|
||||
room_id: RoomId,
|
||||
path: StatePath,
|
||||
},
|
||||
PermissionDelete {
|
||||
room_id: RoomId,
|
||||
path: StatePath,
|
||||
permission: StatePermissionKey
|
||||
},
|
||||
// Groups really should have a way to add permissions by user
|
||||
@@ -197,7 +219,10 @@ pub enum ClientMessage {
|
||||
bytes: Vec<u8>
|
||||
},
|
||||
// Join and subscribe
|
||||
Join {
|
||||
RoomJoin {
|
||||
room_id: RoomId,
|
||||
},
|
||||
RoomCreate {
|
||||
room_id: RoomId,
|
||||
},
|
||||
SubscribeMessages {
|
||||
@@ -213,11 +238,34 @@ pub enum ClientMessage {
|
||||
}
|
||||
}
|
||||
impl ClientMessage<> {
|
||||
pub fn is_forwardable(&self) -> bool {
|
||||
use ClientMessage::*;
|
||||
match self {
|
||||
MediaUpload{bytes: _}
|
||||
| Auth{
|
||||
username: _,
|
||||
password: _
|
||||
}
|
||||
| UserCreate {
|
||||
username: _,
|
||||
password: _,
|
||||
}
|
||||
// In the future challenges might be forwardable but right now they are
|
||||
// only used for signups and auth is local.
|
||||
| ChallengeAnswer {
|
||||
response: _
|
||||
} => false,
|
||||
|
||||
_ => true
|
||||
}
|
||||
|
||||
}
|
||||
pub fn get_relevance(&self) -> Option<Relevance> {
|
||||
use ClientMessage::*;
|
||||
Some(match self {
|
||||
Message {
|
||||
body: _,
|
||||
id: _,
|
||||
room_id
|
||||
// These don't necessarily need cloned, it might make sense to make an owned
|
||||
// version of Relevance and a reference version of it
|
||||
@@ -256,14 +304,26 @@ impl ClientMessage<> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize,Deserialize)]
|
||||
#[derive(Serialize,Deserialize,Debug)]
|
||||
pub enum ServerError {
|
||||
InvalidPermission,
|
||||
// Server can specify the required challenge
|
||||
ChallengeInvitecode,
|
||||
ChallengeInviteCode,
|
||||
NotAuthenticated,
|
||||
AlreadyAuthenticated,
|
||||
NotInChallenge
|
||||
AuthenticationFailed,
|
||||
NotInChallenge,
|
||||
NotJoined(RoomId),
|
||||
RoomExists(RoomId),
|
||||
RoomNotFound(RoomId),
|
||||
ValueNotFound(StatePath),
|
||||
MismatchedTypes,
|
||||
DeleteNotEmpty(StatePath),
|
||||
RemoveLastOwner,
|
||||
MessageNotForwardable,
|
||||
UserAlreadyExists,
|
||||
Generic
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -276,6 +336,8 @@ pub enum ServerMessage {
|
||||
Error(ServerError),
|
||||
// Returned on read
|
||||
State(StatePath,StateValue),
|
||||
// Returned on permission read
|
||||
StatePermission(StatePath,crate::state::PermissionTable),
|
||||
// Returned on subscribe, forwards state change message to client
|
||||
StateChange(TaggedMessage),
|
||||
OkMessage(MessageId),
|
||||
|
||||
+3
-1
@@ -50,10 +50,12 @@ pub enum ServerResponse {
|
||||
Error(FederationError),
|
||||
State(StateValue),
|
||||
Media(Uuid,#[serde(with = "serde_bytes")] Vec<u8>),
|
||||
ValidGroups(User,Vec<StatePermissionKey>)
|
||||
ValidGroups(User,Vec<StatePermissionKey>),
|
||||
Ok
|
||||
}
|
||||
|
||||
#[derive(Serialize,Deserialize)]
|
||||
pub enum FederationError {
|
||||
InvalidPermission,
|
||||
ServerError(crate::client::ServerError)
|
||||
}
|
||||
|
||||
+11
-6
@@ -8,22 +8,23 @@ pub mod message;
|
||||
// Room coordinates and originating server
|
||||
#[derive(Serialize,Deserialize,Clone,Debug,Hash,PartialEq,Eq)]
|
||||
pub struct RoomId{
|
||||
coordinates: Vec<i64>,
|
||||
server: Option<String>
|
||||
pub coordinates: Vec<i64>,
|
||||
//pub server: Option<String>
|
||||
}
|
||||
|
||||
#[derive(Serialize,Deserialize,Clone,Hash,Eq,PartialEq,Debug)]
|
||||
pub struct User {
|
||||
name: String,
|
||||
pub name: String,
|
||||
// Should be a fqdn probably, dunno if its worth enforcing that here
|
||||
server: String
|
||||
pub server: String
|
||||
}
|
||||
|
||||
#[derive(Serialize,Deserialize,Clone,Hash,Eq,PartialEq,Debug)]
|
||||
pub struct Group {
|
||||
name: String,
|
||||
pub name: String,
|
||||
// Should be a fqdn probably, dunno if its worth enforcing that here
|
||||
server: String
|
||||
// Dont need probably because this is already covered in the target field
|
||||
//pub server: String
|
||||
}
|
||||
|
||||
// Might want to standardize this, idk which powers would be useful though
|
||||
@@ -36,3 +37,7 @@ pub struct GroupPower(String);
|
||||
pub struct Role {
|
||||
name: String
|
||||
}
|
||||
|
||||
|
||||
#[derive(Serialize,Deserialize,Clone,Hash,Eq,PartialEq,Debug)]
|
||||
pub struct ServerAddr(pub String);
|
||||
|
||||
+20
-2
@@ -1,7 +1,7 @@
|
||||
use crate::RoomId;
|
||||
use crate::client::ClientMessage;
|
||||
use crate::state::StatePath;
|
||||
use crate::User;
|
||||
use crate::{User,ServerAddr};
|
||||
use time::OffsetDateTime;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -16,6 +16,7 @@ pub struct MessageId(u64);
|
||||
|
||||
#[derive(Serialize,Deserialize,Clone,Debug)]
|
||||
pub struct TaggedMessage {
|
||||
// How do I return message IDs
|
||||
pub message: ClientMessage,
|
||||
#[serde(with="time::serde::timestamp")]
|
||||
pub client_timestamp: OffsetDateTime,
|
||||
@@ -23,7 +24,8 @@ pub struct TaggedMessage {
|
||||
pub server_timestamp: OffsetDateTime,
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub signature: Box<[u8]>,
|
||||
pub user: User
|
||||
pub user: User,
|
||||
pub target: ServerAddr
|
||||
}
|
||||
impl TaggedMessage {
|
||||
pub fn verify(&self) -> Result<bool,VerificationError> {
|
||||
@@ -42,6 +44,22 @@ pub enum Relevance {
|
||||
Post(User)
|
||||
}
|
||||
|
||||
//#[derive(Serialize,Deserialize,Clone,Debug)]
|
||||
//pub struct Message {
|
||||
// pub body: String,
|
||||
// pub room_id: RoomId,
|
||||
// pub target: ServerAddr,
|
||||
// pub message_id: MessageId,
|
||||
// #[serde(with="time::serde::timestamp")]
|
||||
// pub client_timestamp: OffsetDateTime,
|
||||
// #[serde(with="time::serde::timestamp")]
|
||||
// pub server_timestamp: OffsetDateTime,
|
||||
// #[serde(with = "serde_bytes")]
|
||||
// pub signature: Box<[u8]>,
|
||||
// pub user: User,
|
||||
//
|
||||
//}
|
||||
|
||||
//#[derive(Serialize,Deserialize,Debug,Clone)]
|
||||
//pub struct Message {
|
||||
// body: String,
|
||||
|
||||
+71
-7
@@ -1,9 +1,25 @@
|
||||
use serde::{Deserialize,Serialize};
|
||||
use std::collections::{HashMap,HashSet};
|
||||
use crate::{User,Group,Role};
|
||||
#[derive(Serialize,Deserialize,Clone,Debug)]
|
||||
#[derive(Serialize,Deserialize,Clone,Debug,PartialEq)]
|
||||
pub enum StateValue {
|
||||
String(String),
|
||||
Binary(Vec<u8>)
|
||||
Binary(Vec<u8>),
|
||||
// A directory in a readable format to allow for something like `ls`
|
||||
// on clients
|
||||
Directory(HashMap<String,PermissionTable>),
|
||||
}
|
||||
|
||||
impl StateValue {
|
||||
pub fn push_other(&mut self, other: &StateValue) -> Result<(),()> {
|
||||
match (self,other) {
|
||||
(StateValue::String(s),StateValue::String(o)) => s.push_str(o),
|
||||
(StateValue::Binary(b),StateValue::Binary(o)) => b.extend_from_slice(o),
|
||||
_ => return Err(())
|
||||
}
|
||||
Ok(())
|
||||
|
||||
}
|
||||
}
|
||||
#[derive(Serialize,Deserialize,Debug,Clone)]
|
||||
pub enum StateType {
|
||||
@@ -18,20 +34,68 @@ pub enum StatePermissionKey {
|
||||
User(User),
|
||||
Group(Group),
|
||||
Role(Group,Role),
|
||||
// Only the server can edit these. Potentially users with admin permissions
|
||||
// too.
|
||||
Server,
|
||||
// Give everyone a permission. Useful for making a file server readable
|
||||
Everyone,
|
||||
// Give permissions to the room operator/admin. This is kept track of
|
||||
// in the room state. Can operators remove other operators?
|
||||
Operator,
|
||||
//This is a string for now but could get compiled later into an actual regex
|
||||
//should be able to match on name and server
|
||||
UserRegex(String)
|
||||
}
|
||||
#[derive(Serialize,Deserialize,Clone,Debug)]
|
||||
#[derive(Serialize,Deserialize,Clone,Debug,PartialEq,Eq)]
|
||||
pub enum StatePermissionValue {
|
||||
None,
|
||||
Read,
|
||||
ReadWrite,
|
||||
// Can only set value, does not allow appending
|
||||
Write
|
||||
Write,
|
||||
Owner
|
||||
}
|
||||
impl StatePermissionValue {
|
||||
// Binary relation
|
||||
// Maybe could have overloaded a comparison operator for this
|
||||
//
|
||||
// Owner
|
||||
// |
|
||||
// |
|
||||
// ReadWrite
|
||||
// / \
|
||||
// Read Write
|
||||
pub fn allows(&self, other: &StatePermissionValue) -> bool {
|
||||
use StatePermissionValue::*;
|
||||
match (self,other) {
|
||||
(Read,Read) => true,
|
||||
(ReadWrite,Read) => true,
|
||||
(Write,Write) => true,
|
||||
(ReadWrite,Write) => true,
|
||||
// Owner allows everything
|
||||
(Owner,_) => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Serialize,Deserialize,Clone,Debug,PartialEq,Eq)]
|
||||
pub struct StatePermission(pub StatePermissionKey,pub StatePermissionValue);
|
||||
|
||||
#[derive(Serialize,Deserialize,Clone,Debug,PartialEq)]
|
||||
pub struct PermissionTable(pub HashMap<StatePermissionKey,StatePermissionValue>);
|
||||
|
||||
impl PermissionTable {
|
||||
pub fn get_allowed(&self, needed: &StatePermissionValue) -> HashSet<StatePermissionKey> {
|
||||
let mut set = HashSet::new();
|
||||
for (key,perm) in self.0.iter() {
|
||||
if perm.allows(&needed) {
|
||||
set.insert(key.clone());
|
||||
}
|
||||
}
|
||||
set
|
||||
|
||||
}
|
||||
}
|
||||
#[derive(Serialize,Deserialize,Clone,Debug)]
|
||||
pub struct StatePermission(StatePermissionKey,StatePermissionValue);
|
||||
|
||||
#[derive(Serialize,Deserialize,Clone,Debug,Hash,PartialEq,Eq)]
|
||||
pub struct StatePath(Vec<String>);
|
||||
pub struct StatePath(pub Vec<String>);
|
||||
|
||||
Reference in New Issue
Block a user