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 uuid::Uuid;
|
||||||
use crate::message::{MessageId,TaggedMessage,VerificationError,Relevance};
|
use crate::message::{MessageId,TaggedMessage,VerificationError,Relevance};
|
||||||
use crate::state::{StateType,StateValue,StatePath,StatePermission,StatePermissionKey};
|
use crate::state::{self,StateType,StateValue,StatePath,StatePermission,StatePermissionKey};
|
||||||
use crate::{RoomId,Group,Role,User,GroupPower};
|
use crate::{RoomId,Group,Role,User,GroupPower,ServerAddr};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
|
|
||||||
@@ -13,19 +13,19 @@ use time::OffsetDateTime;
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Serialize,Deserialize,Clone,Debug)]
|
//#[derive(Serialize,Deserialize,Clone,Debug)]
|
||||||
/// This exists solely to allow the DB to finish tagging the message. It is every field of a
|
///// 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
|
///// message except for the ID which can be created using a Postgres serial type
|
||||||
pub struct InsertableClientMessage {
|
//pub struct InsertableClientMessage {
|
||||||
pub message: ClientMessage,
|
// pub message: ClientMessage,
|
||||||
#[serde(with="time::serde::timestamp")]
|
// #[serde(with="time::serde::timestamp")]
|
||||||
pub client_timestamp: OffsetDateTime,
|
// pub client_timestamp: OffsetDateTime,
|
||||||
#[serde(with="time::serde::timestamp")]
|
// #[serde(with="time::serde::timestamp")]
|
||||||
pub server_timestamp: OffsetDateTime,
|
// pub server_timestamp: OffsetDateTime,
|
||||||
#[serde(with = "serde_bytes")]
|
// #[serde(with = "serde_bytes")]
|
||||||
pub signature: Box<[u8]>,
|
// pub signature: Box<[u8]>,
|
||||||
pub user: User
|
// pub user: User
|
||||||
}
|
//}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Serialize,Deserialize,Clone,Debug)]
|
#[derive(Serialize,Deserialize,Clone,Debug)]
|
||||||
@@ -39,17 +39,26 @@ pub struct SignedClientMessage {
|
|||||||
// Can I reserialize the message and check the signature that way??
|
// 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
|
// 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
|
// bytes using serde. I'll write something better at some point
|
||||||
|
//
|
||||||
|
// Should this be optional?
|
||||||
#[serde(with = "serde_bytes")]
|
#[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 {
|
impl SignedClientMessage {
|
||||||
pub fn tag(self, username: User) -> InsertableClientMessage {
|
pub fn tag(self, username: User, servername: ServerAddr) -> TaggedMessage {
|
||||||
InsertableClientMessage {
|
TaggedMessage {
|
||||||
message: self.message,
|
message: self.message,
|
||||||
client_timestamp: self.timestamp,
|
client_timestamp: self.timestamp,
|
||||||
server_timestamp: OffsetDateTime::now_utc(),
|
server_timestamp: OffsetDateTime::now_utc(),
|
||||||
signature: self.signature,
|
signature: self.signature,
|
||||||
user: username,
|
user: username,
|
||||||
|
target: self.target.unwrap_or(servername)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn verify(&self) -> Result<bool,VerificationError> {
|
pub fn verify(&self) -> Result<bool,VerificationError> {
|
||||||
@@ -59,7 +68,6 @@ impl SignedClientMessage {
|
|||||||
|
|
||||||
#[derive(Serialize,Deserialize,Clone,Debug)]
|
#[derive(Serialize,Deserialize,Clone,Debug)]
|
||||||
pub enum ClientMessage {
|
pub enum ClientMessage {
|
||||||
|
|
||||||
Auth{
|
Auth{
|
||||||
username: String,
|
username: String,
|
||||||
password: String
|
password: String
|
||||||
@@ -79,7 +87,13 @@ pub enum ClientMessage {
|
|||||||
// Should it be one message type or mutiple?
|
// Should it be one message type or mutiple?
|
||||||
Message {
|
Message {
|
||||||
body: String,
|
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
|
// Private message/invite mechanism
|
||||||
MessagePost {
|
MessagePost {
|
||||||
@@ -100,8 +114,8 @@ pub enum ClientMessage {
|
|||||||
StateCreate {
|
StateCreate {
|
||||||
room_id: RoomId,
|
room_id: RoomId,
|
||||||
path: StatePath,
|
path: StatePath,
|
||||||
ty: StateType,
|
content: StateValue,
|
||||||
permissions: Option<Vec<StatePermission>>
|
permissions: Option<state::PermissionTable>
|
||||||
},
|
},
|
||||||
StateWrite {
|
StateWrite {
|
||||||
room_id: RoomId,
|
room_id: RoomId,
|
||||||
@@ -127,9 +141,17 @@ pub enum ClientMessage {
|
|||||||
path: StatePath
|
path: StatePath
|
||||||
},
|
},
|
||||||
PermissionAdd {
|
PermissionAdd {
|
||||||
|
room_id: RoomId,
|
||||||
|
path: StatePath,
|
||||||
permission: StatePermission
|
permission: StatePermission
|
||||||
},
|
},
|
||||||
|
PermissionRead {
|
||||||
|
room_id: RoomId,
|
||||||
|
path: StatePath,
|
||||||
|
},
|
||||||
PermissionDelete {
|
PermissionDelete {
|
||||||
|
room_id: RoomId,
|
||||||
|
path: StatePath,
|
||||||
permission: StatePermissionKey
|
permission: StatePermissionKey
|
||||||
},
|
},
|
||||||
// Groups really should have a way to add permissions by user
|
// Groups really should have a way to add permissions by user
|
||||||
@@ -197,7 +219,10 @@ pub enum ClientMessage {
|
|||||||
bytes: Vec<u8>
|
bytes: Vec<u8>
|
||||||
},
|
},
|
||||||
// Join and subscribe
|
// Join and subscribe
|
||||||
Join {
|
RoomJoin {
|
||||||
|
room_id: RoomId,
|
||||||
|
},
|
||||||
|
RoomCreate {
|
||||||
room_id: RoomId,
|
room_id: RoomId,
|
||||||
},
|
},
|
||||||
SubscribeMessages {
|
SubscribeMessages {
|
||||||
@@ -213,11 +238,34 @@ pub enum ClientMessage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl 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> {
|
pub fn get_relevance(&self) -> Option<Relevance> {
|
||||||
use ClientMessage::*;
|
use ClientMessage::*;
|
||||||
Some(match self {
|
Some(match self {
|
||||||
Message {
|
Message {
|
||||||
body: _,
|
body: _,
|
||||||
|
id: _,
|
||||||
room_id
|
room_id
|
||||||
// These don't necessarily need cloned, it might make sense to make an owned
|
// These don't necessarily need cloned, it might make sense to make an owned
|
||||||
// version of Relevance and a reference version of it
|
// 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 {
|
pub enum ServerError {
|
||||||
InvalidPermission,
|
InvalidPermission,
|
||||||
// Server can specify the required challenge
|
// Server can specify the required challenge
|
||||||
ChallengeInvitecode,
|
ChallengeInviteCode,
|
||||||
NotAuthenticated,
|
NotAuthenticated,
|
||||||
AlreadyAuthenticated,
|
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),
|
Error(ServerError),
|
||||||
// Returned on read
|
// Returned on read
|
||||||
State(StatePath,StateValue),
|
State(StatePath,StateValue),
|
||||||
|
// Returned on permission read
|
||||||
|
StatePermission(StatePath,crate::state::PermissionTable),
|
||||||
// Returned on subscribe, forwards state change message to client
|
// Returned on subscribe, forwards state change message to client
|
||||||
StateChange(TaggedMessage),
|
StateChange(TaggedMessage),
|
||||||
OkMessage(MessageId),
|
OkMessage(MessageId),
|
||||||
|
|||||||
+3
-1
@@ -50,10 +50,12 @@ pub enum ServerResponse {
|
|||||||
Error(FederationError),
|
Error(FederationError),
|
||||||
State(StateValue),
|
State(StateValue),
|
||||||
Media(Uuid,#[serde(with = "serde_bytes")] Vec<u8>),
|
Media(Uuid,#[serde(with = "serde_bytes")] Vec<u8>),
|
||||||
ValidGroups(User,Vec<StatePermissionKey>)
|
ValidGroups(User,Vec<StatePermissionKey>),
|
||||||
|
Ok
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize,Deserialize)]
|
#[derive(Serialize,Deserialize)]
|
||||||
pub enum FederationError {
|
pub enum FederationError {
|
||||||
InvalidPermission,
|
InvalidPermission,
|
||||||
|
ServerError(crate::client::ServerError)
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-6
@@ -8,22 +8,23 @@ pub mod message;
|
|||||||
// Room coordinates and originating server
|
// Room coordinates and originating server
|
||||||
#[derive(Serialize,Deserialize,Clone,Debug,Hash,PartialEq,Eq)]
|
#[derive(Serialize,Deserialize,Clone,Debug,Hash,PartialEq,Eq)]
|
||||||
pub struct RoomId{
|
pub struct RoomId{
|
||||||
coordinates: Vec<i64>,
|
pub coordinates: Vec<i64>,
|
||||||
server: Option<String>
|
//pub server: Option<String>
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize,Deserialize,Clone,Hash,Eq,PartialEq,Debug)]
|
#[derive(Serialize,Deserialize,Clone,Hash,Eq,PartialEq,Debug)]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
name: String,
|
pub name: String,
|
||||||
// Should be a fqdn probably, dunno if its worth enforcing that here
|
// 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)]
|
#[derive(Serialize,Deserialize,Clone,Hash,Eq,PartialEq,Debug)]
|
||||||
pub struct Group {
|
pub struct Group {
|
||||||
name: String,
|
pub name: String,
|
||||||
// Should be a fqdn probably, dunno if its worth enforcing that here
|
// 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
|
// Might want to standardize this, idk which powers would be useful though
|
||||||
@@ -36,3 +37,7 @@ pub struct GroupPower(String);
|
|||||||
pub struct Role {
|
pub struct Role {
|
||||||
name: String
|
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::RoomId;
|
||||||
use crate::client::ClientMessage;
|
use crate::client::ClientMessage;
|
||||||
use crate::state::StatePath;
|
use crate::state::StatePath;
|
||||||
use crate::User;
|
use crate::{User,ServerAddr};
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
@@ -16,6 +16,7 @@ pub struct MessageId(u64);
|
|||||||
|
|
||||||
#[derive(Serialize,Deserialize,Clone,Debug)]
|
#[derive(Serialize,Deserialize,Clone,Debug)]
|
||||||
pub struct TaggedMessage {
|
pub struct TaggedMessage {
|
||||||
|
// How do I return message IDs
|
||||||
pub message: ClientMessage,
|
pub message: ClientMessage,
|
||||||
#[serde(with="time::serde::timestamp")]
|
#[serde(with="time::serde::timestamp")]
|
||||||
pub client_timestamp: OffsetDateTime,
|
pub client_timestamp: OffsetDateTime,
|
||||||
@@ -23,7 +24,8 @@ pub struct TaggedMessage {
|
|||||||
pub server_timestamp: OffsetDateTime,
|
pub server_timestamp: OffsetDateTime,
|
||||||
#[serde(with = "serde_bytes")]
|
#[serde(with = "serde_bytes")]
|
||||||
pub signature: Box<[u8]>,
|
pub signature: Box<[u8]>,
|
||||||
pub user: User
|
pub user: User,
|
||||||
|
pub target: ServerAddr
|
||||||
}
|
}
|
||||||
impl TaggedMessage {
|
impl TaggedMessage {
|
||||||
pub fn verify(&self) -> Result<bool,VerificationError> {
|
pub fn verify(&self) -> Result<bool,VerificationError> {
|
||||||
@@ -42,6 +44,22 @@ pub enum Relevance {
|
|||||||
Post(User)
|
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)]
|
//#[derive(Serialize,Deserialize,Debug,Clone)]
|
||||||
//pub struct Message {
|
//pub struct Message {
|
||||||
// body: String,
|
// body: String,
|
||||||
|
|||||||
+71
-7
@@ -1,9 +1,25 @@
|
|||||||
use serde::{Deserialize,Serialize};
|
use serde::{Deserialize,Serialize};
|
||||||
|
use std::collections::{HashMap,HashSet};
|
||||||
use crate::{User,Group,Role};
|
use crate::{User,Group,Role};
|
||||||
#[derive(Serialize,Deserialize,Clone,Debug)]
|
#[derive(Serialize,Deserialize,Clone,Debug,PartialEq)]
|
||||||
pub enum StateValue {
|
pub enum StateValue {
|
||||||
String(String),
|
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)]
|
#[derive(Serialize,Deserialize,Debug,Clone)]
|
||||||
pub enum StateType {
|
pub enum StateType {
|
||||||
@@ -18,20 +34,68 @@ pub enum StatePermissionKey {
|
|||||||
User(User),
|
User(User),
|
||||||
Group(Group),
|
Group(Group),
|
||||||
Role(Group,Role),
|
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
|
//This is a string for now but could get compiled later into an actual regex
|
||||||
//should be able to match on name and server
|
//should be able to match on name and server
|
||||||
UserRegex(String)
|
UserRegex(String)
|
||||||
}
|
}
|
||||||
#[derive(Serialize,Deserialize,Clone,Debug)]
|
#[derive(Serialize,Deserialize,Clone,Debug,PartialEq,Eq)]
|
||||||
pub enum StatePermissionValue {
|
pub enum StatePermissionValue {
|
||||||
None,
|
None,
|
||||||
Read,
|
Read,
|
||||||
ReadWrite,
|
ReadWrite,
|
||||||
// Can only set value, does not allow appending
|
// 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)]
|
#[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