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:
2026-05-24 22:56:15 -07:00
parent a3f5470549
commit c2b7575ff4
5 changed files with 193 additions and 42 deletions
+88 -26
View File
@@ -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),