diff --git a/src/client.rs b/src/client.rs index 294fc1e..96e73df 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,8 +1,9 @@ use uuid::Uuid; -use crate::message::{MessageId,Message}; +use crate::message::{MessageId,TaggedMessage,VerificationError,Relevance}; use crate::state::{StateType,StateValue,StatePath,StatePermission,StatePermissionKey}; use crate::{RoomId,Group,Role,User,GroupPower}; use serde::{Deserialize, Serialize}; +use time::OffsetDateTime; //StatePath [String] //octal is fine for now @@ -11,17 +12,28 @@ use serde::{Deserialize, Serialize}; //RoomId [i64] + #[derive(Serialize,Deserialize,Clone,Debug)] -pub struct TaggedClientMessage { - message: ClientMessage, +/// 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")] - signature: Box<[u8]>, - user: User + pub signature: Box<[u8]>, + pub user: User } + #[derive(Serialize,Deserialize,Clone,Debug)] pub struct SignedClientMessage { pub message: ClientMessage, + // timestamp sent by client protects against replay attacks by untrusted server + #[serde(with="time::serde::timestamp")] + pub timestamp: OffsetDateTime, // Should I enforce this being a ecdsa signature? // What is the signature of???? // Can I reserialize the message and check the signature that way?? @@ -30,6 +42,20 @@ pub struct SignedClientMessage { #[serde(with = "serde_bytes")] pub signature: Box<[u8]> } +impl SignedClientMessage { + pub fn tag(self, username: User) -> InsertableClientMessage { + InsertableClientMessage { + message: self.message, + client_timestamp: self.timestamp, + server_timestamp: OffsetDateTime::now_utc(), + signature: self.signature, + user: username, + } + } + pub fn verify(&self) -> Result { + unimplemented!() + } +} #[derive(Serialize,Deserialize,Clone,Debug)] pub enum ClientMessage { @@ -53,6 +79,7 @@ pub enum ClientMessage { // Should it be one message type or mutiple? Message { body: String, + room_id: RoomId }, // Private message/invite mechanism MessagePost { @@ -61,34 +88,42 @@ pub enum ClientMessage { }, // Replace the body of the message with a new one MessageEdit { + room_id: RoomId, body: String, id: MessageId }, MessageDelete { + room_id: RoomId, id: MessageId }, // State Actions StateCreate { + room_id: RoomId, path: StatePath, ty: StateType, permissions: Option> }, StateWrite { + room_id: RoomId, path: StatePath, content: StateValue }, StateDelete { + room_id: RoomId, path: StatePath, }, StateAppend { + room_id: RoomId, path: StatePath, content: StateValue }, StateMove { + room_id: RoomId, path: StatePath, target: StatePath, }, StateRead { + room_id: RoomId, path: StatePath }, PermissionAdd { @@ -177,6 +212,49 @@ pub enum ClientMessage { end: MessageId, } } +impl ClientMessage<> { + pub fn get_relevance(&self) -> Option { + use ClientMessage::*; + Some(match self { + Message { + body: _, + 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 + } => Relevance::Message(room_id.clone()), + // Private message/invite mechanism + MessagePost { + body: _, + user + } => Relevance::Post(user.clone()), + // Replace the body of the message with a new one + MessageEdit { + room_id, + body: _, + id: _ + } => Relevance::Message(room_id.clone()), + MessageDelete { + room_id, + id: _, + } => Relevance::Message(room_id.clone()), + StateWrite { + room_id, + path, + content: _ + } => Relevance::State(room_id.clone(),path.clone()), + StateDelete { + room_id, + path, + } => Relevance::State(room_id.clone(),path.clone()), + StateAppend { + room_id, + path, + content: _ + } => Relevance::State(room_id.clone(),path.clone()), + _ => return None + }) + } +} #[derive(Serialize,Deserialize)] pub enum ServerError { @@ -184,16 +262,22 @@ pub enum ServerError { // Server can specify the required challenge ChallengeInvitecode, NotAuthenticated, + AlreadyAuthenticated, + NotInChallenge } #[derive(Serialize,Deserialize)] pub enum ServerMessage { // Returned on fetch or naturally from subscribe - Messages(Vec), + // This should be + Messages(Vec), MediaUploaded(Uuid), Error(ServerError), - // Returned both on read and from subscribe - State(StateValue), + // Returned on read + State(StatePath,StateValue), + // Returned on subscribe, forwards state change message to client + StateChange(TaggedMessage), + OkMessage(MessageId), Ok, } diff --git a/src/federation.rs b/src/federation.rs index e89804c..626b0c7 100644 --- a/src/federation.rs +++ b/src/federation.rs @@ -2,9 +2,8 @@ use serde::{Deserialize, Serialize}; use uuid::Uuid; use crate::state::{StatePath,StateValue,StatePermissionKey}; -use crate::message::Message; use crate::{RoomId,User}; -use crate::client::TaggedClientMessage; +use crate::message::TaggedMessage; // How do remote subscriptions work? // Server pushes subscription to remote // remote keeps track of list, filters on message broadcast @@ -43,11 +42,11 @@ pub enum ServerRequest { }, //NOTE: It doesn't make sense to forward every kind of message // But duplicating work here doesn't make sense - ForwardedMessage(TaggedClientMessage) + ForwardedMessage(TaggedMessage) } #[derive(Serialize,Deserialize)] pub enum ServerResponse { - Messages(Vec), + Messages(Vec), Error(FederationError), State(StateValue), Media(Uuid,#[serde(with = "serde_bytes")] Vec), diff --git a/src/lib.rs b/src/lib.rs index d2caa19..bdd48ec 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,7 +6,7 @@ pub mod state; pub mod message; // Room coordinates and originating server -#[derive(Serialize,Deserialize,Clone,Debug)] +#[derive(Serialize,Deserialize,Clone,Debug,Hash,PartialEq,Eq)] pub struct RoomId{ coordinates: Vec, server: Option diff --git a/src/message.rs b/src/message.rs index 5d56b39..96bda87 100644 --- a/src/message.rs +++ b/src/message.rs @@ -1,15 +1,54 @@ +use crate::RoomId; +use crate::client::ClientMessage; +use crate::state::StatePath; use crate::User; use time::OffsetDateTime; use serde::{Deserialize, Serialize}; +pub enum VerificationError { + NoKey +} + + //Monotonically increasing messageID #[derive(Serialize,Deserialize,Debug,Clone)] pub struct MessageId(u64); -#[derive(Serialize,Deserialize,Debug,Clone)] -pub struct Message { - body: String, - signature: Box<[u8]>, - sender: User, - timestamp: OffsetDateTime +#[derive(Serialize,Deserialize,Clone,Debug)] +pub struct TaggedMessage { + 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 } +impl TaggedMessage { + pub fn verify(&self) -> Result { + unimplemented!() + } + // Returns room name or state path + pub fn get_relevance(&self) -> Option { + self.message.get_relevance() + } +} + +#[derive(PartialEq,Eq,Hash,Clone,Debug)] +pub enum Relevance { + Message(RoomId), + State(RoomId,StatePath), + Post(User) +} + +//#[derive(Serialize,Deserialize,Debug,Clone)] +//pub struct Message { +// body: String, +// signature: Box<[u8]>, +// sender: User, +// // Used for signature check +// client_timestamp: OffsetDateTime, +// // Used for ordering messages +// server_timestamp: OffsetDateTime, +//} diff --git a/src/state.rs b/src/state.rs index 29191af..624420e 100644 --- a/src/state.rs +++ b/src/state.rs @@ -33,5 +33,5 @@ pub enum StatePermissionValue { #[derive(Serialize,Deserialize,Clone,Debug)] pub struct StatePermission(StatePermissionKey,StatePermissionValue); -#[derive(Serialize,Deserialize,Clone,Debug)] +#[derive(Serialize,Deserialize,Clone,Debug,Hash,PartialEq,Eq)] pub struct StatePath(Vec);