Compare commits
19 commits
f039b9a6e1
...
99fd0a9433
Author | SHA1 | Date | |
---|---|---|---|
99fd0a9433 | |||
69483c3513 | |||
21e5240f74 | |||
48474c3878 | |||
ad50f83cf7 | |||
b3dcb80a6f | |||
a9c41dcd31 | |||
c17cdd888a | |||
0f0dcc6f3a | |||
61dbde2ad5 | |||
0d99c753ed | |||
c2e635f9c7 | |||
d7dd730a88 | |||
21f4449da0 | |||
3938b2032d | |||
5235ca9308 | |||
3c9f92a1ab | |||
b95003257d | |||
a71a059feb |
17 changed files with 788 additions and 198 deletions
97
Cargo.lock
generated
97
Cargo.lock
generated
|
@ -11,6 +11,18 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arrayref"
|
||||||
|
version = "0.3.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arrayvec"
|
||||||
|
version = "0.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-trait"
|
name = "async-trait"
|
||||||
version = "0.1.57"
|
version = "0.1.57"
|
||||||
|
@ -34,6 +46,29 @@ version = "1.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "blake3"
|
||||||
|
version = "1.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a08e53fc5a564bb15bfe6fae56bd71522205f1f91893f9c0116edad6496c183f"
|
||||||
|
dependencies = [
|
||||||
|
"arrayref",
|
||||||
|
"arrayvec",
|
||||||
|
"cc",
|
||||||
|
"cfg-if",
|
||||||
|
"constant_time_eq",
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block-buffer"
|
||||||
|
version = "0.10.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.11.0"
|
version = "3.11.0"
|
||||||
|
@ -52,6 +87,12 @@ version = "1.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db"
|
checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.0.73"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
|
@ -73,6 +114,12 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "constant_time_eq"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation-sys"
|
name = "core-foundation-sys"
|
||||||
version = "0.8.3"
|
version = "0.8.3"
|
||||||
|
@ -112,6 +159,27 @@ dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crypto-common"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
"typenum",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "digest"
|
||||||
|
version = "0.10.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506"
|
||||||
|
dependencies = [
|
||||||
|
"block-buffer",
|
||||||
|
"crypto-common",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fs2"
|
name = "fs2"
|
||||||
version = "0.4.3"
|
version = "0.4.3"
|
||||||
|
@ -131,6 +199,16 @@ dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generic-array"
|
||||||
|
version = "0.14.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
|
||||||
|
dependencies = [
|
||||||
|
"typenum",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.7"
|
version = "0.2.7"
|
||||||
|
@ -164,6 +242,7 @@ dependencies = [
|
||||||
name = "harsh_server"
|
name = "harsh_server"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"blake3",
|
||||||
"chrono",
|
"chrono",
|
||||||
"harsh_common",
|
"harsh_common",
|
||||||
"rand",
|
"rand",
|
||||||
|
@ -508,6 +587,12 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "subtle"
|
||||||
|
version = "2.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.99"
|
version = "1.0.99"
|
||||||
|
@ -572,12 +657,24 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typenum"
|
||||||
|
version = "1.15.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.3"
|
version = "1.0.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf"
|
checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.10.0+wasi-snapshot-preview1"
|
version = "0.10.0+wasi-snapshot-preview1"
|
||||||
|
|
18
README.md
Normal file
18
README.md
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# Harsh
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
An embedded Harmony server implementation written in rust.
|
||||||
|
|
||||||
|
> **Warning**
|
||||||
|
> This project is in early design phase, it is non conforming and very few features are implemented.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
To launch the server, ensure your rustup toolchain is up to date and use the `start-server.sh` script
|
||||||
|
|
||||||
|
To launch the debug client, use the `start-client.sh` script
|
BIN
assets/icon_256.png
Normal file
BIN
assets/icon_256.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
BIN
assets/icon_512.png
Normal file
BIN
assets/icon_512.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 113 KiB |
|
@ -57,13 +57,37 @@ pub fn parse(input: &str) -> Option<Command> {
|
||||||
let content = parts.next()?;
|
let content = parts.next()?;
|
||||||
ClientRequest::new_message_set_content(channel_id, id, content)
|
ClientRequest::new_message_set_content(channel_id, id, content)
|
||||||
}
|
}
|
||||||
|
"usrls" => ClientRequest::new_user_list(),
|
||||||
|
"usradd" => {
|
||||||
|
let name = parts.next()?;
|
||||||
|
let pass = parts.next()?;
|
||||||
|
ClientRequest::new_user_create(name, pass)
|
||||||
|
}
|
||||||
|
"usrdel" => {
|
||||||
|
let id = parts.next()?.parse().ok()?;
|
||||||
|
ClientRequest::new_user_delete(id)
|
||||||
|
}
|
||||||
|
"usrgname" => {
|
||||||
|
let id = parts.next()?.parse().ok()?;
|
||||||
|
ClientRequest::new_user_get_name(id)
|
||||||
|
}
|
||||||
|
"usrsname" => {
|
||||||
|
let id = parts.next()?.parse().ok()?;
|
||||||
|
let name = parts.next()?;
|
||||||
|
ClientRequest::new_user_set_name(id, name)
|
||||||
|
}
|
||||||
|
"usrspass" => {
|
||||||
|
let id = parts.next()?.parse().ok()?;
|
||||||
|
let pass = parts.next()?;
|
||||||
|
ClientRequest::new_user_set_pass(id, pass)
|
||||||
|
}
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(Command::Request(command))
|
Some(Command::Request(command))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const CMDS: &'static [Description] = &[
|
pub const CMDS: &[Description] = &[
|
||||||
// all commands
|
// all commands
|
||||||
Description::new("help", &[], "returns a help message"),
|
Description::new("help", &[], "returns a help message"),
|
||||||
Description::new(
|
Description::new(
|
||||||
|
@ -85,6 +109,12 @@ pub const CMDS: &'static [Description] = &[
|
||||||
&["channel_id", "id", "content"],
|
&["channel_id", "id", "content"],
|
||||||
"set a message's content",
|
"set a message's content",
|
||||||
),
|
),
|
||||||
|
Description::new("usrls", &[], "list users"),
|
||||||
|
Description::new("usradd", &["name", "pass"], "add a user"),
|
||||||
|
Description::new("usrdel", &["id"], "delete a user"),
|
||||||
|
Description::new("usrgname", &["id"], "get a user name"),
|
||||||
|
Description::new("usrsname", &["id", "name"], "set a user name"),
|
||||||
|
Description::new("usrspass", &["id", "pass"], "set a user pass"),
|
||||||
];
|
];
|
||||||
|
|
||||||
pub fn smart_split(input: &str) -> Vec<String> {
|
pub fn smart_split(input: &str) -> Vec<String> {
|
||||||
|
@ -103,12 +133,8 @@ pub fn smart_split(input: &str) -> Vec<String> {
|
||||||
}
|
}
|
||||||
|
|
||||||
match char {
|
match char {
|
||||||
'\\' => {
|
'\\' => ignoring = true,
|
||||||
ignoring = true;
|
'"' => capturing = !capturing,
|
||||||
}
|
|
||||||
'"' => {
|
|
||||||
capturing = !capturing;
|
|
||||||
}
|
|
||||||
' ' if !capturing => {
|
' ' if !capturing => {
|
||||||
result.push(current);
|
result.push(current);
|
||||||
current = String::new();
|
current = String::new();
|
||||||
|
|
|
@ -3,13 +3,13 @@ use std::{
|
||||||
process::exit,
|
process::exit,
|
||||||
};
|
};
|
||||||
|
|
||||||
use harsh_common::ServerRequest;
|
use harsh_common::ServerEvent;
|
||||||
use tokio::{
|
use tokio::{
|
||||||
io::{stdin, AsyncBufReadExt, AsyncWriteExt, BufReader},
|
io::{stdin, AsyncBufReadExt, AsyncWriteExt, BufReader},
|
||||||
net::TcpStream,
|
net::TcpStream,
|
||||||
};
|
};
|
||||||
|
|
||||||
const ADDRESS: &'static str = "localhost:42069";
|
const ADDRESS: &str = "localhost:42000";
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
|
@ -21,13 +21,10 @@ async fn main() {
|
||||||
let mut reader = BufReader::new(reader);
|
let mut reader = BufReader::new(reader);
|
||||||
loop {
|
loop {
|
||||||
let mut line = String::new();
|
let mut line = String::new();
|
||||||
match reader.read_line(&mut line).await {
|
if let Ok(0) = reader.read_line(&mut line).await {
|
||||||
Ok(0) => {
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
if let Some(parsed) = ServerRequest::try_parse(&line) {
|
if let Some(parsed) = ServerEvent::try_parse(&line) {
|
||||||
println!("[main/info] received '{parsed:?}'");
|
println!("[main/info] received '{parsed:?}'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,12 @@ pub struct Ping {
|
||||||
pub content: String,
|
pub content: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Authenticate {
|
||||||
|
pub id: u64,
|
||||||
|
pub pass: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ChannelList {}
|
pub struct ChannelList {}
|
||||||
|
|
||||||
|
@ -92,6 +98,8 @@ pub struct UserSetPass {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ClientRequest {
|
pub enum ClientRequest {
|
||||||
Ping(Ping),
|
Ping(Ping),
|
||||||
|
Authenticate(Authenticate),
|
||||||
|
|
||||||
ChannelList(ChannelList),
|
ChannelList(ChannelList),
|
||||||
ChannelCreate(ChannelCreate),
|
ChannelCreate(ChannelCreate),
|
||||||
ChannelDelete(ChannelDelete),
|
ChannelDelete(ChannelDelete),
|
||||||
|
@ -117,6 +125,10 @@ impl ClientRequest {
|
||||||
Self::Ping(Ping { content })
|
Self::Ping(Ping { content })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_authenticate(id: u64, pass: String) -> Self {
|
||||||
|
Self::Authenticate(Authenticate { id, pass })
|
||||||
|
}
|
||||||
|
|
||||||
pub fn new_channel_list() -> Self {
|
pub fn new_channel_list() -> Self {
|
||||||
Self::ChannelList(ChannelList {})
|
Self::ChannelList(ChannelList {})
|
||||||
}
|
}
|
||||||
|
@ -162,56 +174,54 @@ impl ClientRequest {
|
||||||
content,
|
content,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
pub fn new_user_list() -> Self {
|
||||||
|
Self::UserList(UserList {})
|
||||||
|
}
|
||||||
|
pub fn new_user_create(name: String, pass: String) -> Self {
|
||||||
|
Self::UserCreate(UserCreate { name, pass })
|
||||||
|
}
|
||||||
|
pub fn new_user_delete(id: u64) -> Self {
|
||||||
|
Self::UserDelete(UserDelete { id })
|
||||||
|
}
|
||||||
|
pub fn new_user_get_name(id: u64) -> Self {
|
||||||
|
Self::UserGetName(UserGetName { id })
|
||||||
|
}
|
||||||
|
pub fn new_user_set_name(id: u64, name: String) -> Self {
|
||||||
|
Self::UserSetName(UserSetName { id, name })
|
||||||
|
}
|
||||||
|
pub fn new_user_set_pass(id: u64, pass: String) -> Self {
|
||||||
|
Self::UserSetPass(UserSetPass { id, pass })
|
||||||
|
}
|
||||||
|
|
||||||
pub fn try_parse(line: &str) -> Option<Self> {
|
pub fn try_parse(line: &str) -> Option<Self> {
|
||||||
use repr::Command::*;
|
use repr::Command::*;
|
||||||
let command: repr::Command = serde_json::from_str(line).ok()?;
|
let command: repr::Command = serde_json::from_str(line).ok()?;
|
||||||
let mapped = match command {
|
let mapped = match command {
|
||||||
ping { content } => Self::Ping(Ping { content }),
|
ping { content } => Self::new_ping(content),
|
||||||
channel_list {} => Self::ChannelList(ChannelList {}),
|
authenticate { id, pass } => Self::new_authenticate(id, pass),
|
||||||
channel_create { name } => Self::ChannelCreate(ChannelCreate { name }),
|
channel_list {} => Self::new_channel_list(),
|
||||||
channel_delete { id: channel_id } => {
|
channel_create { name } => Self::new_channel_create(name),
|
||||||
Self::ChannelDelete(ChannelDelete { id: channel_id })
|
channel_delete { id } => Self::new_channel_delete(id),
|
||||||
}
|
channel_get_name { id } => Self::new_channel_get_name(id),
|
||||||
channel_get_name { id: channel_id } => {
|
channel_set_name { id, name } => Self::new_channel_set_name(id, name),
|
||||||
Self::ChannelGetName(ChannelGetName { id: channel_id })
|
message_list { channel_id } => Self::new_message_list(channel_id),
|
||||||
}
|
|
||||||
channel_set_name {
|
|
||||||
id: channel_id,
|
|
||||||
name,
|
|
||||||
} => Self::ChannelSetName(ChannelSetName {
|
|
||||||
id: channel_id,
|
|
||||||
name,
|
|
||||||
}),
|
|
||||||
message_list { channel_id } => Self::MessageList(MessageList { channel_id }),
|
|
||||||
message_create {
|
message_create {
|
||||||
channel_id,
|
channel_id,
|
||||||
content,
|
content,
|
||||||
} => Self::MessageCreate(MessageCreate {
|
} => Self::new_message_create(channel_id, content),
|
||||||
channel_id,
|
message_delete { id, channel_id } => Self::new_message_delete(channel_id, id),
|
||||||
content,
|
message_get_content { id, channel_id } => Self::new_message_get_content(channel_id, id),
|
||||||
}),
|
|
||||||
message_delete { id, channel_id } => {
|
|
||||||
Self::MessageDelete(MessageDelete { id, channel_id })
|
|
||||||
}
|
|
||||||
message_get_content { id, channel_id } => {
|
|
||||||
Self::MessageGetContent(MessageGetContent { id, channel_id })
|
|
||||||
}
|
|
||||||
message_set_content {
|
message_set_content {
|
||||||
id,
|
id,
|
||||||
channel_id,
|
channel_id,
|
||||||
content,
|
content,
|
||||||
} => Self::MessageSetContent(MessageSetContent {
|
} => Self::new_message_set_content(channel_id, id, content),
|
||||||
content,
|
user_list {} => Self::new_user_list(),
|
||||||
id,
|
user_create { name, pass } => Self::new_user_create(name, pass),
|
||||||
channel_id,
|
user_delete { id } => Self::new_user_delete(id),
|
||||||
}),
|
user_get_name { id } => Self::new_user_get_name(id),
|
||||||
user_list {} => Self::UserList(UserList {}),
|
user_set_name { id, name } => Self::new_user_set_name(id, name),
|
||||||
user_create { name, pass } => Self::UserCreate(UserCreate { name, pass }),
|
user_set_pass { id, pass } => Self::new_user_set_pass(id, pass),
|
||||||
user_delete { id } => Self::UserDelete(UserDelete { id }),
|
|
||||||
user_get_name { id } => Self::UserGetName(UserGetName { id }),
|
|
||||||
user_set_name { id, name } => Self::UserSetName(UserSetName { id, name }),
|
|
||||||
user_set_pass { id, pass } => Self::UserSetPass(UserSetPass { id, pass }),
|
|
||||||
};
|
};
|
||||||
Some(mapped)
|
Some(mapped)
|
||||||
}
|
}
|
||||||
|
@ -220,6 +230,7 @@ impl ClientRequest {
|
||||||
use repr::Command::*;
|
use repr::Command::*;
|
||||||
let mapped = match self {
|
let mapped = match self {
|
||||||
Self::Ping(Ping { content }) => ping { content },
|
Self::Ping(Ping { content }) => ping { content },
|
||||||
|
Self::Authenticate(Authenticate { id, pass }) => authenticate { id, pass },
|
||||||
Self::ChannelList(ChannelList {}) => repr::Command::channel_list {},
|
Self::ChannelList(ChannelList {}) => repr::Command::channel_list {},
|
||||||
Self::ChannelCreate(ChannelCreate { name }) => channel_create { name },
|
Self::ChannelCreate(ChannelCreate { name }) => channel_create { name },
|
||||||
Self::ChannelDelete(ChannelDelete { id: channel_id }) => {
|
Self::ChannelDelete(ChannelDelete { id: channel_id }) => {
|
||||||
|
@ -280,6 +291,10 @@ mod repr {
|
||||||
ping {
|
ping {
|
||||||
content: String,
|
content: String,
|
||||||
},
|
},
|
||||||
|
authenticate {
|
||||||
|
id: u64,
|
||||||
|
pass: String,
|
||||||
|
},
|
||||||
channel_list {},
|
channel_list {},
|
||||||
channel_create {
|
channel_create {
|
||||||
name: String,
|
name: String,
|
||||||
|
|
|
@ -10,5 +10,5 @@ mod tests {
|
||||||
pub use client::ClientRequest;
|
pub use client::ClientRequest;
|
||||||
pub mod client;
|
pub mod client;
|
||||||
|
|
||||||
pub use server::ServerRequest;
|
pub use server::ServerEvent;
|
||||||
pub mod server;
|
pub mod server;
|
||||||
|
|
|
@ -61,7 +61,9 @@ pub struct MessageSetContent {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct UserList {}
|
pub struct UserList {
|
||||||
|
pub users: Vec<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct UserCreate {
|
pub struct UserCreate {
|
||||||
|
@ -92,7 +94,7 @@ pub struct UserSetPass {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ServerRequest {
|
pub enum ServerEvent {
|
||||||
Pong(Pong),
|
Pong(Pong),
|
||||||
|
|
||||||
ChannelCreate(ChannelCreate),
|
ChannelCreate(ChannelCreate),
|
||||||
|
@ -115,7 +117,7 @@ pub enum ServerRequest {
|
||||||
UserSetPass(UserSetPass),
|
UserSetPass(UserSetPass),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ServerRequest {
|
impl ServerEvent {
|
||||||
pub fn new_pong(content: String) -> Self {
|
pub fn new_pong(content: String) -> Self {
|
||||||
Self::Pong(Pong { content })
|
Self::Pong(Pong { content })
|
||||||
}
|
}
|
||||||
|
@ -174,6 +176,30 @@ impl ServerRequest {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_user_list(users: Vec<u64>) -> Self {
|
||||||
|
Self::UserList(UserList { users })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_user_create(id: u64, name: String) -> Self {
|
||||||
|
Self::UserCreate(UserCreate { id, name })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_user_delete(id: u64) -> Self {
|
||||||
|
Self::UserDelete(UserDelete { id })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_user_get_name(id: u64, name: Option<String>) -> Self {
|
||||||
|
Self::UserGetName(UserGetName { id, name })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_user_set_name(id: u64, name: String) -> Self {
|
||||||
|
Self::UserSetName(UserSetName { id, name })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_user_set_pass(id: u64) -> Self {
|
||||||
|
Self::UserSetPass(UserSetPass { id })
|
||||||
|
}
|
||||||
|
|
||||||
pub fn try_parse(line: &str) -> Option<Self> {
|
pub fn try_parse(line: &str) -> Option<Self> {
|
||||||
use repr::Command::*;
|
use repr::Command::*;
|
||||||
let command: repr::Command = serde_json::from_str(line).ok()?;
|
let command: repr::Command = serde_json::from_str(line).ok()?;
|
||||||
|
@ -221,7 +247,7 @@ impl ServerRequest {
|
||||||
content,
|
content,
|
||||||
id,
|
id,
|
||||||
}),
|
}),
|
||||||
user_list {} => Self::UserList(UserList {}),
|
user_list { users } => Self::UserList(UserList { users }),
|
||||||
user_create { id, name } => Self::UserCreate(UserCreate { id, name }),
|
user_create { id, name } => Self::UserCreate(UserCreate { id, name }),
|
||||||
user_delete { id } => Self::UserDelete(UserDelete { id }),
|
user_delete { id } => Self::UserDelete(UserDelete { id }),
|
||||||
user_get_name { id, name } => Self::UserGetName(UserGetName { id, name }),
|
user_get_name { id, name } => Self::UserGetName(UserGetName { id, name }),
|
||||||
|
@ -278,7 +304,7 @@ impl ServerRequest {
|
||||||
id,
|
id,
|
||||||
content,
|
content,
|
||||||
},
|
},
|
||||||
Self::UserList(UserList {}) => user_list {},
|
Self::UserList(UserList { users }) => user_list { users },
|
||||||
Self::UserCreate(UserCreate { id, name }) => user_create { id, name },
|
Self::UserCreate(UserCreate { id, name }) => user_create { id, name },
|
||||||
Self::UserDelete(UserDelete { id }) => user_delete { id },
|
Self::UserDelete(UserDelete { id }) => user_delete { id },
|
||||||
Self::UserGetName(UserGetName { id, name }) => user_get_name { id, name },
|
Self::UserGetName(UserGetName { id, name }) => user_get_name { id, name },
|
||||||
|
@ -342,7 +368,9 @@ mod repr {
|
||||||
id: u64,
|
id: u64,
|
||||||
content: String,
|
content: String,
|
||||||
},
|
},
|
||||||
user_list {},
|
user_list {
|
||||||
|
users: Vec<u64>,
|
||||||
|
},
|
||||||
user_create {
|
user_create {
|
||||||
id: u64,
|
id: u64,
|
||||||
name: String,
|
name: String,
|
||||||
|
|
|
@ -15,3 +15,4 @@ tokio = { version = "1.20", features = ["full"] }
|
||||||
harsh_common = { path = "../harsh-common/" }
|
harsh_common = { path = "../harsh-common/" }
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
|
blake3 = "1.3.1"
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
use harsh_common::{client, server, ClientRequest, ServerRequest};
|
use harsh_common::{client, server, ClientRequest, ServerEvent};
|
||||||
use telecomande::{Processor, Remote};
|
use telecomande::{Processor, Remote};
|
||||||
|
|
||||||
use crate::{Addr, Id, SessionCmd, SessionProc, StorageCmd, StorageProc};
|
use crate::{
|
||||||
|
sessions::SessionExt, storage::Perm, Addr, Id, SecurityCmd, SecurityProc, SessionCmd,
|
||||||
|
SessionProc, StorageCmd, StorageProc,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum GatewayCmd {
|
pub enum GatewayCmd {
|
||||||
|
@ -12,161 +15,246 @@ pub enum GatewayCmd {
|
||||||
pub struct GatewayProc {
|
pub struct GatewayProc {
|
||||||
sessions: Remote<SessionProc>,
|
sessions: Remote<SessionProc>,
|
||||||
storage: Remote<StorageProc>,
|
storage: Remote<StorageProc>,
|
||||||
|
security: Remote<SecurityProc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use client::*;
|
||||||
|
|
||||||
impl GatewayProc {
|
impl GatewayProc {
|
||||||
async fn handle_request(&mut self, address: Addr, request: ClientRequest) {
|
async fn handle_request(&mut self, addr: Addr, request: ClientRequest) -> Result<(), String> {
|
||||||
use client as c;
|
use client::*;
|
||||||
use ClientRequest::*;
|
use ClientRequest as CR;
|
||||||
|
|
||||||
|
// auth-free API
|
||||||
|
let request = match request {
|
||||||
|
CR::Ping(ping) => return self.on_ping(ping, addr),
|
||||||
|
CR::Authenticate(authenticate) => {
|
||||||
|
return self.on_authenticate(authenticate, addr).await
|
||||||
|
}
|
||||||
|
_ => request,
|
||||||
|
};
|
||||||
|
|
||||||
|
// let user = self.sessions.get_user(addr.clone()).await.ok_or("owno")?;
|
||||||
|
|
||||||
|
// auth API
|
||||||
match request {
|
match request {
|
||||||
Ping(c::Ping { content }) => self.on_ping(content, address),
|
CR::Ping(_) | CR::Authenticate(_) => unreachable!(),
|
||||||
|
|
||||||
ChannelCreate(c::ChannelCreate { name }) => self.on_channel_create(name).await,
|
CR::ChannelCreate(req) => self.on_channel_create(req).await,
|
||||||
ChannelDelete(c::ChannelDelete { id }) => self.on_channel_delete(id),
|
CR::ChannelDelete(req) => todo!(), //self.on_channel_delete(req, user).await,
|
||||||
ChannelList(c::ChannelList {}) => self.on_channel_list(address).await,
|
CR::ChannelList(req) => self.on_channel_list(req, addr).await,
|
||||||
ChannelGetName(c::ChannelGetName { id }) => self.on_channel_get_name(id, address).await,
|
CR::ChannelGetName(req) => self.on_channel_get_name(req, addr).await,
|
||||||
ChannelSetName(c::ChannelSetName { id, name }) => self.on_channel_set_name(id, name),
|
CR::ChannelSetName(req) => self.on_channel_set_name(req),
|
||||||
|
|
||||||
MessageList(c::MessageList { channel_id }) => {
|
CR::MessageList(req) => self.on_message_list(req, addr).await,
|
||||||
self.on_message_list(channel_id, address).await
|
CR::MessageCreate(req) => self.on_message_create(req).await,
|
||||||
}
|
CR::MessageDelete(req) => self.on_message_delete(req),
|
||||||
MessageCreate(c::MessageCreate {
|
CR::MessageGetContent(req) => self.on_message_get_content(req, addr).await,
|
||||||
channel_id,
|
CR::MessageSetContent(req) => self.on_message_set_content(req),
|
||||||
content,
|
|
||||||
}) => self.on_message_create(channel_id, content).await,
|
|
||||||
MessageDelete(c::MessageDelete { channel_id, id }) => {
|
|
||||||
self.on_message_delete(channel_id, id)
|
|
||||||
}
|
|
||||||
MessageGetContent(c::MessageGetContent { channel_id, id }) => {
|
|
||||||
self.on_message_get_content(channel_id, id, address).await
|
|
||||||
}
|
|
||||||
|
|
||||||
MessageSetContent(c::MessageSetContent {
|
CR::UserList(req) => self.on_user_list(req, addr).await,
|
||||||
channel_id,
|
CR::UserCreate(req) => self.on_user_create(req).await,
|
||||||
id,
|
CR::UserDelete(req) => self.on_user_delete(req),
|
||||||
content,
|
CR::UserGetName(req) => self.on_user_get_name(req, addr).await,
|
||||||
}) => {
|
CR::UserSetName(req) => self.on_user_set_name(req),
|
||||||
self.on_message_set_content(channel_id, id, content);
|
CR::UserSetPass(req) => self.on_user_set_pass(req, addr),
|
||||||
}
|
};
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: user
|
async fn on_authenticate(
|
||||||
UserList(c::UserList {}) => {
|
&mut self,
|
||||||
todo!()
|
Authenticate { id, pass }: Authenticate,
|
||||||
}
|
address: Addr,
|
||||||
UserCreate(c::UserCreate { name, pass }) => {
|
) -> Result<(), String> {
|
||||||
todo!()
|
let (cmd, rec) = SecurityCmd::new_authenticate(id.into(), pass);
|
||||||
}
|
self.security.send(cmd).unwrap();
|
||||||
UserDelete(c::UserDelete { id }) => {
|
if rec.await.unwrap() {
|
||||||
todo!()
|
let command = SessionCmd::new_set_user(address, Some(id.into()));
|
||||||
}
|
self.sessions.send(command).unwrap();
|
||||||
UserGetName(c::UserGetName { id }) => {
|
} else {
|
||||||
todo!()
|
Err("Invalid password")?;
|
||||||
}
|
};
|
||||||
UserSetName(c::UserSetName { id, name }) => {
|
Ok(())
|
||||||
todo!()
|
}
|
||||||
}
|
|
||||||
UserSetPass(c::UserSetPass { id, pass }) => {
|
pub fn new(
|
||||||
todo!()
|
sessions: Remote<SessionProc>,
|
||||||
}
|
storage: Remote<StorageProc>,
|
||||||
|
security: Remote<SecurityProc>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
sessions,
|
||||||
|
storage,
|
||||||
|
security,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(sessions: Remote<SessionProc>, storage: Remote<StorageProc>) -> Self {
|
fn on_ping(&mut self, Ping { content }: Ping, address: Addr) -> Result<(), String> {
|
||||||
Self { sessions, storage }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_ping(&mut self, content: String, address: Addr) {
|
|
||||||
println!("[gateway/PING] '{content:?}'");
|
println!("[gateway/PING] '{content:?}'");
|
||||||
let request = ServerRequest::Pong(server::Pong { content });
|
let request = ServerEvent::Pong(server::Pong { content });
|
||||||
let command = SessionCmd::new_send(address, request);
|
let command = SessionCmd::new_send(address, request);
|
||||||
self.sessions.send(command).unwrap();
|
self.sessions.send(command).unwrap();
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_channel_create(&mut self, name: String) {
|
async fn on_channel_create(&mut self, ChannelCreate { name }: ChannelCreate) {
|
||||||
let (cmd, rec) = StorageCmd::new_channel_create(name.clone());
|
let (cmd, rec) = StorageCmd::new_channel_create(name.clone());
|
||||||
self.storage.send(cmd).unwrap();
|
self.storage.send(cmd).unwrap();
|
||||||
let id = rec.await.unwrap().to_u64();
|
let id = rec.await.unwrap().to_u64();
|
||||||
let request = ServerRequest::new_channel_create(id, name);
|
let request = ServerEvent::new_channel_create(id, name);
|
||||||
let command = SessionCmd::new_broadcast(request);
|
let command = SessionCmd::new_broadcast(request);
|
||||||
self.sessions.send(command).unwrap();
|
self.sessions.send(command).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_channel_delete(&mut self, id: u64) {
|
async fn on_channel_delete(&mut self, ChannelDelete { id }: ChannelDelete, user: Id) {
|
||||||
|
// TODO: verify is OP
|
||||||
|
let (cmd, req) = SecurityCmd::new_verify(user, Perm::OpChannel(id.into()));
|
||||||
|
self.security.send(cmd).unwrap();
|
||||||
|
req.await.unwrap();
|
||||||
let command = StorageCmd::new_channel_delete(id.into());
|
let command = StorageCmd::new_channel_delete(id.into());
|
||||||
self.storage.send(command).unwrap();
|
self.storage.send(command).unwrap();
|
||||||
let request = ServerRequest::new_channel_delete(id);
|
let request = ServerEvent::new_channel_delete(id);
|
||||||
let command = SessionCmd::new_broadcast(request);
|
let command = SessionCmd::new_broadcast(request);
|
||||||
self.sessions.send(command).unwrap();
|
self.sessions.send(command).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_channel_list(&mut self, address: Addr) {
|
async fn on_channel_list(&mut self, _: ChannelList, address: Addr) {
|
||||||
let (cmd, rec) = StorageCmd::new_channel_list();
|
let (cmd, rec) = StorageCmd::new_channel_list();
|
||||||
self.storage.send(cmd).unwrap();
|
self.storage.send(cmd).unwrap();
|
||||||
let channels = rec.await.unwrap().iter().map(|id| id.to_u64()).collect();
|
let channels = rec.await.unwrap().iter().map(|id| id.to_u64()).collect();
|
||||||
let request = ServerRequest::new_channel_list(channels);
|
let request = ServerEvent::new_channel_list(channels);
|
||||||
let command = SessionCmd::new_send(address, request);
|
let command = SessionCmd::new_send(address, request);
|
||||||
self.sessions.send(command).unwrap();
|
self.sessions.send(command).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_channel_get_name(&mut self, id: u64, address: Addr) {
|
async fn on_channel_get_name(&mut self, ChannelGetName { id }: ChannelGetName, address: Addr) {
|
||||||
let (cmd, rec) = StorageCmd::new_channel_get_name(id.into());
|
let (cmd, rec) = StorageCmd::new_channel_get_name(id.into());
|
||||||
self.storage.send(cmd).unwrap();
|
self.storage.send(cmd).unwrap();
|
||||||
let name = rec.await.unwrap();
|
let name = rec.await.unwrap();
|
||||||
let request = ServerRequest::new_channel_get_name(id, name);
|
let request = ServerEvent::new_channel_get_name(id, name);
|
||||||
let command = SessionCmd::new_send(address, request);
|
let command = SessionCmd::new_send(address, request);
|
||||||
self.sessions.send(command).unwrap();
|
self.sessions.send(command).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_channel_set_name(&mut self, id: u64, name: String) {
|
fn on_channel_set_name(&mut self, ChannelSetName { id, name }: ChannelSetName) {
|
||||||
let command = StorageCmd::new_channel_set_name(id.into(), name.clone());
|
let command = StorageCmd::new_channel_set_name(id.into(), name.clone());
|
||||||
self.storage.send(command).unwrap();
|
self.storage.send(command).unwrap();
|
||||||
let request = ServerRequest::new_channel_set_name(id, name);
|
let request = ServerEvent::new_channel_set_name(id, name);
|
||||||
let command = SessionCmd::new_broadcast(request);
|
let command = SessionCmd::new_broadcast(request);
|
||||||
self.sessions.send(command).unwrap();
|
self.sessions.send(command).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_message_list(&mut self, channel_id: u64, address: Addr) {
|
async fn on_message_list(&mut self, MessageList { channel_id }: MessageList, address: Addr) {
|
||||||
let (cmd, rec) = StorageCmd::new_message_list(channel_id.into());
|
let (cmd, rec) = StorageCmd::new_message_list(channel_id.into());
|
||||||
self.storage.send(cmd).unwrap();
|
self.storage.send(cmd).unwrap();
|
||||||
let messages = rec.await.unwrap().iter().map(Id::to_u64).collect();
|
let messages = rec.await.unwrap().iter().map(Id::to_u64).collect();
|
||||||
let request = ServerRequest::new_message_list(channel_id, messages);
|
let request = ServerEvent::new_message_list(channel_id, messages);
|
||||||
let command = SessionCmd::new_send(address, request);
|
let command = SessionCmd::new_send(address, request);
|
||||||
self.sessions.send(command).unwrap();
|
self.sessions.send(command).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_message_create(&mut self, channel_id: u64, content: String) {
|
async fn on_message_create(
|
||||||
|
&mut self,
|
||||||
|
MessageCreate {
|
||||||
|
channel_id,
|
||||||
|
content,
|
||||||
|
}: MessageCreate,
|
||||||
|
) {
|
||||||
let (cmd, rec) = StorageCmd::new_message_create(channel_id.into(), content.clone());
|
let (cmd, rec) = StorageCmd::new_message_create(channel_id.into(), content.clone());
|
||||||
self.storage.send(cmd).unwrap();
|
self.storage.send(cmd).unwrap();
|
||||||
let id = rec.await.unwrap();
|
let id = rec.await.unwrap();
|
||||||
let request = ServerRequest::new_message_create(channel_id, id.to_u64(), content);
|
let request = ServerEvent::new_message_create(channel_id, id.to_u64(), content);
|
||||||
let command = SessionCmd::new_broadcast(request);
|
let command = SessionCmd::new_broadcast(request);
|
||||||
self.sessions.send(command).unwrap();
|
self.sessions.send(command).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_message_delete(&mut self, channel_id: u64, id: u64) {
|
fn on_message_delete(&mut self, MessageDelete { channel_id, id }: MessageDelete) {
|
||||||
let command = StorageCmd::new_message_delete(channel_id.into(), id.into());
|
let command = StorageCmd::new_message_delete(channel_id.into(), id.into());
|
||||||
self.storage.send(command).unwrap();
|
self.storage.send(command).unwrap();
|
||||||
let request = ServerRequest::new_message_delete(channel_id, id);
|
let request = ServerEvent::new_message_delete(channel_id, id);
|
||||||
let command = SessionCmd::new_broadcast(request);
|
let command = SessionCmd::new_broadcast(request);
|
||||||
self.sessions.send(command).unwrap();
|
self.sessions.send(command).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_message_get_content(&mut self, channel_id: u64, id: u64, address: Addr) {
|
async fn on_message_get_content(
|
||||||
|
&mut self,
|
||||||
|
MessageGetContent { channel_id, id }: MessageGetContent,
|
||||||
|
address: Addr,
|
||||||
|
) {
|
||||||
let (cmd, rec) = StorageCmd::new_message_get_content(channel_id.into(), id.into());
|
let (cmd, rec) = StorageCmd::new_message_get_content(channel_id.into(), id.into());
|
||||||
self.storage.send(cmd).unwrap();
|
self.storage.send(cmd).unwrap();
|
||||||
let request = ServerRequest::new_message_get_content(channel_id, id, rec.await.unwrap());
|
let request = ServerEvent::new_message_get_content(channel_id, id, rec.await.unwrap());
|
||||||
let command = SessionCmd::new_send(address, request);
|
let command = SessionCmd::new_send(address, request);
|
||||||
self.sessions.send(command).unwrap();
|
self.sessions.send(command).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_message_set_content(&mut self, channel_id: u64, id: u64, content: String) {
|
fn on_message_set_content(
|
||||||
|
&mut self,
|
||||||
|
MessageSetContent {
|
||||||
|
channel_id,
|
||||||
|
id,
|
||||||
|
content,
|
||||||
|
}: MessageSetContent,
|
||||||
|
) {
|
||||||
let command =
|
let command =
|
||||||
StorageCmd::new_message_set_content(channel_id.into(), id.into(), content.clone());
|
StorageCmd::new_message_set_content(channel_id.into(), id.into(), content.clone());
|
||||||
self.storage.send(command).unwrap();
|
self.storage.send(command).unwrap();
|
||||||
let request = ServerRequest::new_message_set_content(channel_id, id, content);
|
let request = ServerEvent::new_message_set_content(channel_id, id, content);
|
||||||
let command = SessionCmd::new_broadcast(request);
|
let command = SessionCmd::new_broadcast(request);
|
||||||
self.sessions.send(command).unwrap();
|
self.sessions.send(command).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn on_user_set_pass(&mut self, UserSetPass { id, pass }: UserSetPass, address: Addr) {
|
||||||
|
let command = StorageCmd::new_user_set_pass(id.into(), pass);
|
||||||
|
self.storage.send(command).unwrap();
|
||||||
|
let request = ServerEvent::new_user_set_pass(id);
|
||||||
|
let command = SessionCmd::new_send(address, request);
|
||||||
|
self.sessions.send(command).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_user_set_name(&mut self, UserSetName { id, name }: UserSetName) {
|
||||||
|
let command = StorageCmd::new_user_set_name(id.into(), name.clone());
|
||||||
|
self.storage.send(command).unwrap();
|
||||||
|
let request = ServerEvent::new_user_set_name(id, name);
|
||||||
|
let command = SessionCmd::new_broadcast(request);
|
||||||
|
self.sessions.send(command).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn on_user_get_name(&mut self, UserGetName { id }: UserGetName, address: Addr) {
|
||||||
|
let (cmd, rec) = StorageCmd::new_user_get_name(id.into());
|
||||||
|
self.storage.send(cmd).unwrap();
|
||||||
|
let name = rec.await.unwrap();
|
||||||
|
let request = ServerEvent::new_user_get_name(id, name);
|
||||||
|
let command = SessionCmd::new_send(address, request);
|
||||||
|
self.sessions.send(command).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_user_delete(&mut self, UserDelete { id }: UserDelete) {
|
||||||
|
let command = StorageCmd::new_user_delete(id.into());
|
||||||
|
self.storage.send(command).unwrap();
|
||||||
|
let request = ServerEvent::new_user_delete(id);
|
||||||
|
let command = SessionCmd::new_broadcast(request);
|
||||||
|
self.sessions.send(command).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn on_user_create(&mut self, UserCreate { name, pass }: UserCreate) {
|
||||||
|
let (cmd, rec) = StorageCmd::new_user_create(name.clone(), pass);
|
||||||
|
self.storage.send(cmd).unwrap();
|
||||||
|
let id = rec.await.unwrap();
|
||||||
|
let request = ServerEvent::new_user_create(id.into(), name);
|
||||||
|
let command = SessionCmd::new_broadcast(request);
|
||||||
|
self.sessions.send(command).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn on_user_list(&mut self, _: UserList, address: Addr) {
|
||||||
|
let (cmd, rec) = StorageCmd::new_user_list();
|
||||||
|
self.storage.send(cmd).unwrap();
|
||||||
|
let result = rec.await.unwrap().iter().map(Id::to_u64).collect();
|
||||||
|
let request = ServerEvent::new_user_list(result);
|
||||||
|
let command = SessionCmd::new_send(address, request);
|
||||||
|
self.sessions.send(command).unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[telecomande::async_trait]
|
#[telecomande::async_trait]
|
||||||
|
@ -178,9 +266,11 @@ impl Processor for GatewayProc {
|
||||||
GatewayCmd::Request(address, request) => {
|
GatewayCmd::Request(address, request) => {
|
||||||
if let Some(request) = ClientRequest::try_parse(&request) {
|
if let Some(request) = ClientRequest::try_parse(&request) {
|
||||||
println!("[session/info] received command '{request:?}'");
|
println!("[session/info] received command '{request:?}'");
|
||||||
self.handle_request(address, request).await;
|
if let Err(reason) = self.handle_request(address, request).await {
|
||||||
|
eprintln!("[gateway/warn] exception '{reason}'");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
println!("[session/warn] failed to parse command");
|
println!("[session/info] failed to parse command");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GatewayCmd::ClosedConnection(address) => self
|
GatewayCmd::ClosedConnection(address) => self
|
||||||
|
|
|
@ -1,23 +1,31 @@
|
||||||
use telecomande::{Executor, SimpleExecutor};
|
use telecomande::{Executor, SimpleExecutor};
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
|
|
||||||
const ADDRESS: &'static str = "localhost:42069";
|
const ADDRESS: &str = "localhost:42000";
|
||||||
const DB_PATH: &'static str = "./db.test";
|
const DB_PATH: &str = "./db.test";
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
println!("[main/info] starting server ...");
|
println!("[main/info] starting server ...");
|
||||||
|
|
||||||
let sessions = SimpleExecutor::new(SessionProc::default()).spawn();
|
let sessions = SimpleExecutor::new(SessionProc::default()).spawn();
|
||||||
println!("[main/info] spawned sessions");
|
println!("[main/info] spawned sessions");
|
||||||
|
|
||||||
let storage = SimpleExecutor::new(StorageProc::new(DB_PATH)).spawn();
|
let storage = SimpleExecutor::new(StorageProc::new(DB_PATH)).spawn();
|
||||||
println!("[main/info] spawned storage");
|
println!("[main/info] spawned storage");
|
||||||
let gateway =
|
|
||||||
SimpleExecutor::new(GatewayProc::new(sessions.remote(), storage.remote())).spawn();
|
let security = SimpleExecutor::new(SecurityProc::new(storage.remote())).spawn();
|
||||||
|
|
||||||
|
let gateway = SimpleExecutor::new(GatewayProc::new(
|
||||||
|
sessions.remote(),
|
||||||
|
storage.remote(),
|
||||||
|
security.remote(),
|
||||||
|
))
|
||||||
|
.spawn();
|
||||||
println!("[main/info] spawned gateway");
|
println!("[main/info] spawned gateway");
|
||||||
|
|
||||||
let listener = TcpListener::bind(ADDRESS).await.unwrap();
|
let listener = TcpListener::bind(ADDRESS).await.unwrap();
|
||||||
println!("[main/info] listening on '{ADDRESS}' ...");
|
println!("[main/info] listening on '{ADDRESS}' ...");
|
||||||
|
|
||||||
let client_handler = sessions.remote();
|
let client_handler = sessions.remote();
|
||||||
loop {
|
loop {
|
||||||
let (stream, address) = listener.accept().await.unwrap();
|
let (stream, address) = listener.accept().await.unwrap();
|
||||||
|
@ -42,3 +50,6 @@ pub use sessions::{SessionCmd, SessionProc};
|
||||||
|
|
||||||
mod storage;
|
mod storage;
|
||||||
pub use storage::{StorageCmd, StorageProc};
|
pub use storage::{StorageCmd, StorageProc};
|
||||||
|
|
||||||
|
mod security;
|
||||||
|
pub use security::{SecurityCmd, SecurityProc};
|
||||||
|
|
99
harsh-server/src/security.rs
Normal file
99
harsh-server/src/security.rs
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
use telecomande::{Processor, Remote};
|
||||||
|
use tokio::sync::oneshot::{self, Receiver, Sender};
|
||||||
|
|
||||||
|
use crate::{storage::Perm, Id, StorageCmd, StorageProc};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum SecurityCmd {
|
||||||
|
Verify(Id, Perm, Sender<bool>),
|
||||||
|
Authenticate(Id, String, Sender<bool>),
|
||||||
|
StorePass(Id, String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SecurityCmd {
|
||||||
|
pub fn new_verify(user_id: Id, permission: Perm) -> (Self, Receiver<bool>) {
|
||||||
|
let (sender, receiver) = oneshot::channel();
|
||||||
|
let command = Self::Verify(user_id, permission, sender);
|
||||||
|
(command, receiver)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_authenticate(user_id: Id, pass: String) -> (Self, Receiver<bool>) {
|
||||||
|
let (sender, receiver) = oneshot::channel();
|
||||||
|
let command = Self::Authenticate(user_id, pass, sender);
|
||||||
|
(command, receiver)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_store_pass(user_id: Id, pass: String) -> Self {
|
||||||
|
Self::StorePass(user_id, pass)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SecurityProc {
|
||||||
|
storage: Remote<StorageProc>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SecurityProc {
|
||||||
|
pub fn new(storage: Remote<StorageProc>) -> Self {
|
||||||
|
Self { storage }
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_command(&mut self, command: SecurityCmd) {
|
||||||
|
match command {
|
||||||
|
SecurityCmd::Verify(user, perm, sender) => {
|
||||||
|
let (cmd, req) = StorageCmd::new_perm_server_get_op();
|
||||||
|
self.storage.send(cmd).unwrap();
|
||||||
|
let serv_ops = req.await.unwrap();
|
||||||
|
let is_serv_op = serv_ops.into_iter().any(|i| i == user);
|
||||||
|
let result = match (is_serv_op, perm) {
|
||||||
|
(true, _) => true,
|
||||||
|
(false, Perm::OpChannel(chan_id)) => {
|
||||||
|
let (cmd, req) = StorageCmd::new_perm_channel_get_op(chan_id);
|
||||||
|
self.storage.send(cmd).unwrap();
|
||||||
|
let channel_ops = req.await.unwrap();
|
||||||
|
channel_ops.into_iter().any(|i| i == user)
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
sender.send(result).unwrap();
|
||||||
|
}
|
||||||
|
SecurityCmd::Authenticate(user, pass, sender) => {
|
||||||
|
let (cmd, rec) = StorageCmd::new_user_get_pass(user);
|
||||||
|
self.storage.send(cmd).unwrap();
|
||||||
|
let stored = rec.await.unwrap();
|
||||||
|
let result = stored.map(|stored| stored == hash(pass)).unwrap_or(false);
|
||||||
|
sender.send(result).unwrap();
|
||||||
|
}
|
||||||
|
SecurityCmd::StorePass(user, pass) => {
|
||||||
|
let pass = hash(pass);
|
||||||
|
let command = StorageCmd::new_user_set_pass(user, pass);
|
||||||
|
self.storage.send(command).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const SALT: &str = ":)";
|
||||||
|
|
||||||
|
fn hash(input: String) -> String {
|
||||||
|
let hash = blake3::hash((input + SALT).as_bytes());
|
||||||
|
format!("{hash}")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hash() {
|
||||||
|
assert_eq!(
|
||||||
|
&hash("arbre".into()),
|
||||||
|
"c2d3a87dcb76c21a8a935b8e988745f31663c3650a0d3732430eaa323f12ee0f"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[telecomande::async_trait]
|
||||||
|
impl Processor for SecurityProc {
|
||||||
|
type Command = SecurityCmd;
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
async fn handle(&mut self, command: Self::Command) -> Result<(), Self::Error> {
|
||||||
|
self.handle_command(command).await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{collections::HashMap, net::SocketAddr};
|
use std::{collections::HashMap, net::SocketAddr};
|
||||||
|
|
||||||
use harsh_common::ServerRequest;
|
use harsh_common::ServerEvent;
|
||||||
use telecomande::{Processor, Remote};
|
use telecomande::{Processor, Remote};
|
||||||
use tokio::{
|
use tokio::{
|
||||||
io::{AsyncBufReadExt, AsyncWriteExt, BufReader},
|
io::{AsyncBufReadExt, AsyncWriteExt, BufReader},
|
||||||
|
@ -8,33 +8,58 @@ use tokio::{
|
||||||
tcp::{OwnedReadHalf, OwnedWriteHalf},
|
tcp::{OwnedReadHalf, OwnedWriteHalf},
|
||||||
TcpStream,
|
TcpStream,
|
||||||
},
|
},
|
||||||
|
sync::oneshot::{self, Receiver, Sender},
|
||||||
task::JoinHandle,
|
task::JoinHandle,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{gateway, Addr};
|
use crate::{gateway, Addr, Id};
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum SessionCmd {
|
pub enum SessionCmd {
|
||||||
AddSession(TcpStream, SocketAddr, Remote<gateway::GatewayProc>),
|
AddSession(TcpStream, SocketAddr, Remote<gateway::GatewayProc>),
|
||||||
RemoveSession(Addr),
|
RemoveSession(Addr),
|
||||||
Send(Addr, String),
|
Send(Addr, String),
|
||||||
Broadcast(String),
|
Broadcast(String),
|
||||||
|
GetUser(Addr, Sender<Option<Id>>),
|
||||||
|
SetUser(Addr, Option<Id>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SessionCmd {
|
impl SessionCmd {
|
||||||
pub fn new_send(address: Addr, request: ServerRequest) -> Self {
|
pub fn new_add_session(
|
||||||
|
stream: TcpStream,
|
||||||
|
address: SocketAddr,
|
||||||
|
gateway: Remote<gateway::GatewayProc>,
|
||||||
|
) -> Self {
|
||||||
|
Self::AddSession(stream, address, gateway)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_remove_session(address: Addr) -> Self {
|
||||||
|
Self::RemoveSession(address)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_send(address: Addr, request: ServerEvent) -> Self {
|
||||||
let content = request.serialize();
|
let content = request.serialize();
|
||||||
Self::Send(address, content)
|
Self::Send(address, content)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_broadcast(request: ServerRequest) -> Self {
|
pub fn new_broadcast(request: ServerEvent) -> Self {
|
||||||
let content = request.serialize();
|
let content = request.serialize();
|
||||||
Self::Broadcast(content)
|
Self::Broadcast(content)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_get_user(address: Addr) -> (Self, Receiver<Option<Id>>) {
|
||||||
|
let (sender, receiver) = oneshot::channel();
|
||||||
|
let command = Self::GetUser(address, sender);
|
||||||
|
(command, receiver)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_set_user(address: Addr, user: Option<Id>) -> Self {
|
||||||
|
Self::SetUser(address, user)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct SessionProc {
|
pub struct SessionProc {
|
||||||
clients: HashMap<Addr, (OwnedWriteHalf, JoinHandle<()>)>,
|
clients: HashMap<Addr, Client>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SessionProc {
|
impl SessionProc {
|
||||||
|
@ -46,7 +71,7 @@ impl SessionProc {
|
||||||
) {
|
) {
|
||||||
let (reader, writer) = stream.into_split();
|
let (reader, writer) = stream.into_split();
|
||||||
let handle = tokio::spawn(session(address.clone(), reader, remote));
|
let handle = tokio::spawn(session(address.clone(), reader, remote));
|
||||||
self.clients.insert(address, (writer, handle));
|
self.clients.insert(address, Client::new(writer, handle));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,24 +90,29 @@ impl Processor for SessionProc {
|
||||||
}
|
}
|
||||||
SessionCmd::RemoveSession(address) => {
|
SessionCmd::RemoveSession(address) => {
|
||||||
println!("[sessions/info] closed connection from '{address:?}'");
|
println!("[sessions/info] closed connection from '{address:?}'");
|
||||||
if let Some((_writer, handle)) = self.clients.remove(&address) {
|
if let Some(client) = self.clients.remove(&address) {
|
||||||
handle.await.unwrap();
|
client.unwrap().await.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SessionCmd::Send(address, content) => {
|
SessionCmd::Send(address, content) => {
|
||||||
if let Some((client, _)) = self.clients.get_mut(&address) {
|
if let Some(client) = self.clients.get_mut(&address) {
|
||||||
println!("[session/info] sending '{content}' to '{address:?}'");
|
println!("[session/info] sending '{content}' to '{address:?}'");
|
||||||
client.write_all(content.as_bytes()).await.unwrap();
|
client.send(&content).await;
|
||||||
client.write_all(b"\n").await.unwrap();
|
|
||||||
} else {
|
|
||||||
eprintln!("failed to find session with address '{address:?}'")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SessionCmd::Broadcast(content) => {
|
SessionCmd::Broadcast(content) => {
|
||||||
for (client, _) in self.clients.values_mut() {
|
println!("[session/info] broadcasting '{content}'");
|
||||||
println!("[session/info] broadcasting '{content}'");
|
for client in self.clients.values_mut() {
|
||||||
client.write_all(content.as_bytes()).await.unwrap();
|
client.send(&content).await;
|
||||||
client.write_all(b"\n").await.unwrap();
|
}
|
||||||
|
}
|
||||||
|
SessionCmd::GetUser(address, sender) => {
|
||||||
|
let user = self.clients.get_mut(&address).and_then(|c| c.get_user());
|
||||||
|
sender.send(user).unwrap();
|
||||||
|
}
|
||||||
|
SessionCmd::SetUser(address, user) => {
|
||||||
|
if let Some(client) = self.clients.get_mut(&address) {
|
||||||
|
client.set_user(user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -90,6 +120,45 @@ impl Processor for SessionProc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Client {
|
||||||
|
writer: OwnedWriteHalf,
|
||||||
|
handle: JoinHandle<()>,
|
||||||
|
user: Option<Id>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Client {
|
||||||
|
pub fn new(writer: OwnedWriteHalf, handle: JoinHandle<()>) -> Self {
|
||||||
|
let user = None;
|
||||||
|
Self {
|
||||||
|
handle,
|
||||||
|
user,
|
||||||
|
writer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unwrap(self) -> JoinHandle<()> {
|
||||||
|
let Self {
|
||||||
|
writer,
|
||||||
|
handle,
|
||||||
|
user,
|
||||||
|
} = self;
|
||||||
|
drop((writer, user));
|
||||||
|
handle
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn send(&mut self, message: &str) {
|
||||||
|
self.writer.write_all(message.as_bytes()).await.unwrap();
|
||||||
|
self.writer.write_all(b"\n").await.unwrap();
|
||||||
|
}
|
||||||
|
pub fn set_user(&mut self, id: Option<Id>) {
|
||||||
|
self.user = id;
|
||||||
|
}
|
||||||
|
pub fn get_user(&self) -> Option<Id> {
|
||||||
|
self.user
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async fn session(address: Addr, reader: OwnedReadHalf, remote: Remote<gateway::GatewayProc>) {
|
async fn session(address: Addr, reader: OwnedReadHalf, remote: Remote<gateway::GatewayProc>) {
|
||||||
let mut reader = BufReader::new(reader);
|
let mut reader = BufReader::new(reader);
|
||||||
loop {
|
loop {
|
||||||
|
@ -107,3 +176,24 @@ async fn session(address: Addr, reader: OwnedReadHalf, remote: Remote<gateway::G
|
||||||
.send(gateway::GatewayCmd::ClosedConnection(address))
|
.send(gateway::GatewayCmd::ClosedConnection(address))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[telecomande::async_trait]
|
||||||
|
pub trait SessionExt {
|
||||||
|
fn send(&self, cmd: SessionCmd);
|
||||||
|
|
||||||
|
async fn is_logged(&self, address: Addr) -> bool {
|
||||||
|
self.get_user(address).await.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_user(&self, address: Addr) -> Option<Id> {
|
||||||
|
let (cmd, rec) = SessionCmd::new_get_user(address);
|
||||||
|
self.send(cmd);
|
||||||
|
rec.await.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SessionExt for Remote<SessionProc> {
|
||||||
|
fn send(&self, cmd: SessionCmd) {
|
||||||
|
self.send(cmd).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -23,6 +23,12 @@ pub enum StorageCmd {
|
||||||
UserSetName(Id, String),
|
UserSetName(Id, String),
|
||||||
UserGetPass(Id, Sender<Option<String>>),
|
UserGetPass(Id, Sender<Option<String>>),
|
||||||
UserSetPass(Id, String),
|
UserSetPass(Id, String),
|
||||||
|
PermServerAddOp(Id),
|
||||||
|
PermServerRemoveOp(Id),
|
||||||
|
PermServerGetOp(Sender<Vec<Id>>),
|
||||||
|
PermChannelAddOp(Id, Id),
|
||||||
|
PermChannelRemoveOp(Id, Id),
|
||||||
|
PermChannelGetOp(Id, Sender<Vec<Id>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StorageCmd {
|
impl StorageCmd {
|
||||||
|
@ -74,6 +80,70 @@ impl StorageCmd {
|
||||||
pub fn new_message_set_content(channel_id: Id, id: Id, content: String) -> Self {
|
pub fn new_message_set_content(channel_id: Id, id: Id, content: String) -> Self {
|
||||||
Self::MessageSetContent(channel_id, id, content)
|
Self::MessageSetContent(channel_id, id, content)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_user_list() -> (Self, Receiver<Vec<Id>>) {
|
||||||
|
let (sender, receiver) = oneshot::channel();
|
||||||
|
let cmd = Self::UserList(sender);
|
||||||
|
(cmd, receiver)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_user_create(name: String, pass: String) -> (Self, Receiver<Id>) {
|
||||||
|
let (sender, receiver) = oneshot::channel();
|
||||||
|
let cmd = Self::UserCreate(name, pass, sender);
|
||||||
|
(cmd, receiver)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_user_delete(id: Id) -> Self {
|
||||||
|
Self::UserDelete(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_user_get_name(id: Id) -> (Self, Receiver<Option<String>>) {
|
||||||
|
let (sender, receiver) = oneshot::channel();
|
||||||
|
let cmd = Self::UserGetName(id, sender);
|
||||||
|
(cmd, receiver)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_user_set_name(id: Id, name: String) -> Self {
|
||||||
|
Self::UserSetName(id, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_user_get_pass(id: Id) -> (Self, Receiver<Option<String>>) {
|
||||||
|
let (sender, receiver) = oneshot::channel();
|
||||||
|
let cmd = Self::UserGetPass(id, sender);
|
||||||
|
(cmd, receiver)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_user_set_pass(id: Id, pass: String) -> Self {
|
||||||
|
Self::UserSetPass(id, pass)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_perm_server_add_op(user_id: Id) -> Self {
|
||||||
|
Self::PermServerAddOp(user_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_perm_server_remove_op(user_id: Id) -> Self {
|
||||||
|
Self::PermServerRemoveOp(user_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_perm_server_get_op() -> (Self, Receiver<Vec<Id>>) {
|
||||||
|
let (sender, receiver) = oneshot::channel();
|
||||||
|
let cmd = Self::PermServerGetOp(sender);
|
||||||
|
(cmd, receiver)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_perm_channel_add_op(channel_id: Id, user_id: Id) -> Self {
|
||||||
|
Self::PermChannelAddOp(channel_id, user_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_perm_channel_remove_op(channel_id: Id, user_id: Id) -> Self {
|
||||||
|
Self::PermChannelRemoveOp(channel_id, user_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_perm_channel_get_op(channel_id: Id) -> (Self, Receiver<Vec<Id>>) {
|
||||||
|
let (sender, receiver) = oneshot::channel();
|
||||||
|
let command = Self::PermChannelGetOp(channel_id, sender);
|
||||||
|
(command, receiver)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct StorageProc {
|
pub struct StorageProc {
|
||||||
|
@ -158,47 +228,32 @@ impl StorageProc {
|
||||||
//
|
//
|
||||||
// User
|
// User
|
||||||
//
|
//
|
||||||
UserList(sender) => {
|
UserList(sender) => self.on_user_list(sender),
|
||||||
let users = self.list("/users/");
|
UserCreate(name, pass, sender) => self.on_user_create(name, pass, sender),
|
||||||
sender.send(users).unwrap();
|
UserDelete(id) => self.on_user_delete(id),
|
||||||
}
|
UserGetName(id, sender) => self.on_user_get_name(id, sender),
|
||||||
|
UserSetName(id, name) => self.on_user_set_name(id, name),
|
||||||
|
UserGetPass(id, sender) => self.on_user_get_pass(id, sender),
|
||||||
|
UserSetPass(id, pass) => self.on_user_set_pass(id, pass),
|
||||||
|
|
||||||
UserCreate(name, pass, sender) => {
|
//
|
||||||
let user = User::new(name, pass);
|
// Perms
|
||||||
let id = user.get_id();
|
//
|
||||||
sender.send(id).unwrap();
|
PermServerGetOp(sender) => {
|
||||||
|
let result = self.list("/op/serv/".to_string());
|
||||||
|
sender.send(result).unwrap();
|
||||||
}
|
}
|
||||||
|
PermServerAddOp(user_id) => self.set(format!("/op/serv/{user_id}"), true),
|
||||||
UserDelete(id) => {
|
PermServerRemoveOp(user_id) => self.remove(format!("/op/serv/{user_id}")),
|
||||||
self.remove(format!("/users/{id}"));
|
PermChannelAddOp(channel_id, user_id) => {
|
||||||
|
self.set(format!("/op/channels/{channel_id}/{user_id}"), true)
|
||||||
}
|
}
|
||||||
|
PermChannelRemoveOp(channel_id, user_id) => {
|
||||||
UserGetName(id, sender) => {
|
self.remove(format!("/op/channels/{channel_id}/{user_id}"))
|
||||||
let user = self.get::<_, User>(format!("/users/{id}"));
|
|
||||||
let name = user.map(|u| u.get_name().to_string());
|
|
||||||
sender.send(name).unwrap();
|
|
||||||
}
|
}
|
||||||
|
PermChannelGetOp(channel_id, sender) => {
|
||||||
UserSetName(id, name) => {
|
let result = self.list(format!("/op/channels/{channel_id}/"));
|
||||||
let path = format!("/users/{id}");
|
sender.send(result).unwrap();
|
||||||
if let Some(mut user) = self.get::<_, User>(&path) {
|
|
||||||
user.set_name(name);
|
|
||||||
self.set(path, user);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
UserGetPass(id, sender) => {
|
|
||||||
let user = self.get::<_, User>(format!("/users/{id}"));
|
|
||||||
let name = user.map(|u| u.get_pass().to_string());
|
|
||||||
sender.send(name).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
UserSetPass(id, pass) => {
|
|
||||||
let path = format!("/users/{id}");
|
|
||||||
if let Some(mut user) = self.get::<_, User>(&path) {
|
|
||||||
user.set_pass(pass);
|
|
||||||
self.set(path, user);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -219,6 +274,9 @@ impl StorageProc {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_channel_remove(&mut self, id: Id) {
|
fn on_channel_remove(&mut self, id: Id) {
|
||||||
|
for message_id in self.list(format!("/messages/{id}/")) {
|
||||||
|
self.remove(format!("/messages/{id}/{message_id}"))
|
||||||
|
}
|
||||||
self.remove(format!("/channels/{id}"))
|
self.remove(format!("/channels/{id}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -268,6 +326,54 @@ impl StorageProc {
|
||||||
self.set(path, message);
|
self.set(path, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// User
|
||||||
|
//
|
||||||
|
|
||||||
|
fn on_user_list(&mut self, sender: Sender<Vec<Id>>) {
|
||||||
|
let users = self.list("/users/");
|
||||||
|
sender.send(users).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_user_create(&mut self, name: String, pass: String, sender: Sender<Id>) {
|
||||||
|
let user = User::new(name, pass);
|
||||||
|
let id = user.get_id();
|
||||||
|
self.set(format!("/users/{id}"), user);
|
||||||
|
sender.send(id).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_user_delete(&mut self, id: Id) {
|
||||||
|
self.remove(format!("/users/{id}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_user_get_name(&mut self, id: Id, sender: Sender<Option<String>>) {
|
||||||
|
let user = self.get::<_, User>(format!("/users/{id}"));
|
||||||
|
let name = user.map(|u| u.get_name().to_string());
|
||||||
|
sender.send(name).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_user_set_name(&mut self, id: Id, name: String) {
|
||||||
|
let path = format!("/users/{id}");
|
||||||
|
if let Some(mut user) = self.get::<_, User>(&path) {
|
||||||
|
user.set_name(name);
|
||||||
|
self.set(path, user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_user_get_pass(&mut self, id: Id, sender: Sender<Option<String>>) {
|
||||||
|
let user = self.get::<_, User>(format!("/users/{id}"));
|
||||||
|
let name = user.map(|u| u.get_pass().to_string());
|
||||||
|
sender.send(name).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_user_set_pass(&mut self, id: Id, pass: String) {
|
||||||
|
let path = format!("/users/{id}");
|
||||||
|
if let Some(mut user) = self.get::<_, User>(&path) {
|
||||||
|
user.set_pass(pass);
|
||||||
|
self.set(path, user);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[telecomande::async_trait]
|
#[telecomande::async_trait]
|
||||||
|
@ -283,7 +389,7 @@ impl Processor for StorageProc {
|
||||||
}
|
}
|
||||||
|
|
||||||
mod models;
|
mod models;
|
||||||
pub use models::{Channel, Message, SerDeser, User};
|
pub use models::{Channel, Message, Perm, SerDeser, User};
|
||||||
|
|
||||||
fn list(db: &Db, path: String) -> Vec<Id> {
|
fn list(db: &Db, path: String) -> Vec<Id> {
|
||||||
let len = path.len();
|
let len = path.len();
|
||||||
|
|
|
@ -114,3 +114,9 @@ where
|
||||||
db.insert(path, bytes).unwrap();
|
db.insert(path, bytes).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
|
||||||
|
pub enum Perm {
|
||||||
|
OpServer,
|
||||||
|
OpChannel(Id),
|
||||||
|
}
|
||||||
|
|
|
@ -43,6 +43,12 @@ impl From<u64> for Id {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Id> for u64 {
|
||||||
|
fn from(input: Id) -> Self {
|
||||||
|
input.to_u64()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_string_convertion() {
|
fn test_string_convertion() {
|
||||||
let id = Id::from_now();
|
let id = Id::from_now();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue