Update crates and change to UUID
This commit is contained in:
11
src/error.rs
11
src/error.rs
@ -172,3 +172,14 @@ impl From<tokio::sync::mpsc::error::SendError<Event>> for Error {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<uuid::Error> for Error {
|
||||
fn from(error: uuid::Error) -> Self {
|
||||
Error {
|
||||
kind: ErrorKind::UsimpError,
|
||||
class: ErrorClass::ClientError,
|
||||
msg: None,
|
||||
desc: Some(error.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ use crate::websocket;
|
||||
use hyper::{body, header, Body, Method, Request, Response, StatusCode};
|
||||
use serde_json::{Map, Value};
|
||||
use std::str::FromStr;
|
||||
use uuid::Uuid;
|
||||
|
||||
async fn endpoint_handler(
|
||||
req: &mut Request<Body>,
|
||||
@ -63,12 +64,12 @@ async fn endpoint_handler(
|
||||
|
||||
let input = InputEnvelope {
|
||||
endpoint,
|
||||
to_domain,
|
||||
to_domain: Uuid::from_str(to_domain.as_str())?,
|
||||
from_domain: match req
|
||||
.headers()
|
||||
.get(header::HeaderName::from_str("From-Domain").unwrap())
|
||||
{
|
||||
Some(val) => Some(val.to_str()?.to_string()),
|
||||
Some(val) => Some(Uuid::from_str(val.to_str()?)?),
|
||||
None => None,
|
||||
},
|
||||
request_nr: None,
|
||||
|
39
src/main.rs
39
src/main.rs
@ -10,6 +10,7 @@ use futures_util::{future::TryFutureExt, stream::Stream};
|
||||
use hyper::server::conn::AddrStream;
|
||||
use hyper::service::{make_service_fn, service_fn};
|
||||
use hyper::Server;
|
||||
use rustls_pemfile;
|
||||
|
||||
mod database;
|
||||
mod error;
|
||||
@ -47,8 +48,11 @@ fn load_certs(filename: &str) -> std::io::Result<Vec<rustls::Certificate>> {
|
||||
.map_err(|e| error(format!("failed to open {}: {}", filename, e)))?;
|
||||
let mut reader = std::io::BufReader::new(certfile);
|
||||
|
||||
rustls::internal::pemfile::certs(&mut reader)
|
||||
.map_err(|_| error("failed to load certificate".into()))
|
||||
Ok(rustls_pemfile::certs(&mut reader)
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|v| rustls::Certificate(v.clone()))
|
||||
.collect())
|
||||
}
|
||||
|
||||
fn load_private_key(filename: &str) -> std::io::Result<rustls::PrivateKey> {
|
||||
@ -56,12 +60,17 @@ fn load_private_key(filename: &str) -> std::io::Result<rustls::PrivateKey> {
|
||||
.map_err(|e| error(format!("failed to open {}: {}", filename, e)))?;
|
||||
let mut reader = std::io::BufReader::new(keyfile);
|
||||
|
||||
let keys = rustls::internal::pemfile::rsa_private_keys(&mut reader)
|
||||
.map_err(|_| error("failed to load private key".into()))?;
|
||||
if keys.len() < 1 {
|
||||
return Err(error("expected a single private key".into()));
|
||||
loop {
|
||||
match rustls_pemfile::read_one(&mut reader).expect("cannot parse private key .pem file") {
|
||||
Some(rustls_pemfile::Item::RSAKey(key)) => return Ok(rustls::PrivateKey(key)),
|
||||
Some(rustls_pemfile::Item::PKCS8Key(key)) => return Ok(rustls::PrivateKey(key)),
|
||||
Some(rustls_pemfile::Item::ECKey(key)) => return Ok(rustls::PrivateKey(key)),
|
||||
None => break,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Ok(keys[0].clone())
|
||||
|
||||
Err(error("unexpected error".into()))
|
||||
}
|
||||
|
||||
fn error(err: String) -> std::io::Error {
|
||||
@ -85,11 +94,17 @@ async fn main() -> Result<(), Error> {
|
||||
let srv1 = server1.serve(service);
|
||||
|
||||
let tls_cfg = {
|
||||
let certs = load_certs("/home/lorenz/Certificates/priv/fullchain.pem").unwrap();
|
||||
let key = load_private_key("/home/lorenz/Certificates/priv/privkey.pem").unwrap();
|
||||
let mut cfg = rustls::ServerConfig::new(rustls::NoClientAuth::new());
|
||||
cfg.set_single_cert(certs, key).unwrap();
|
||||
cfg.set_protocols(&[b"h2".to_vec(), b"http/1.1".to_vec()]);
|
||||
let certs = load_certs("/home/lorenz/Certificates/priv/necronda.net/fullchain.pem").unwrap();
|
||||
let key = load_private_key("/home/lorenz/Certificates/priv/necronda.net/privkey.pem").unwrap();
|
||||
let mut cfg = rustls::ServerConfig::builder()
|
||||
.with_safe_default_cipher_suites()
|
||||
.with_safe_default_kx_groups()
|
||||
.with_protocol_versions(rustls::ALL_VERSIONS)
|
||||
.unwrap()
|
||||
.with_no_client_auth()
|
||||
.with_single_cert(certs, key)
|
||||
.unwrap();
|
||||
cfg.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
|
||||
std::sync::Arc::new(cfg)
|
||||
};
|
||||
|
||||
|
@ -5,6 +5,7 @@ use crate::usimp::*;
|
||||
use rand::Rng;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{from_value, to_value, Value};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
struct Input {
|
||||
@ -26,14 +27,14 @@ pub async fn handle(input: &InputEnvelope, session: Option<Session>) -> Result<V
|
||||
|
||||
async fn authenticate(input: Input, _session: Option<Session>) -> Result<Output, Error> {
|
||||
let backend = database::client().await?;
|
||||
let token;
|
||||
let token: String;
|
||||
let session_id;
|
||||
match backend {
|
||||
database::Client::Postgres(client) => {
|
||||
let res = client
|
||||
.query(
|
||||
"SELECT account_id, domain_id \
|
||||
FROM accounts \
|
||||
FROM account \
|
||||
WHERE account_name = $1",
|
||||
&[&input.name],
|
||||
)
|
||||
@ -46,8 +47,8 @@ async fn authenticate(input: Input, _session: Option<Session>) -> Result<Output,
|
||||
));
|
||||
}
|
||||
let row = &res[0];
|
||||
let account_id: String = row.get(0);
|
||||
let domain_id: String = row.get(1);
|
||||
let account_id: Uuid = row.get(0);
|
||||
let domain_id: Uuid = row.get(1);
|
||||
|
||||
// TODO password check
|
||||
if !input.password.eq("MichaelScott") {
|
||||
@ -58,7 +59,7 @@ async fn authenticate(input: Input, _session: Option<Session>) -> Result<Output,
|
||||
));
|
||||
}
|
||||
|
||||
session_id = usimp::get_id(&[domain_id.as_str(), account_id.as_str()]);
|
||||
session_id = Uuid::new_v4();
|
||||
token = rand::thread_rng()
|
||||
.sample_iter(&rand::distributions::Alphanumeric)
|
||||
.take(256)
|
||||
@ -67,14 +68,14 @@ async fn authenticate(input: Input, _session: Option<Session>) -> Result<Output,
|
||||
|
||||
client
|
||||
.execute(
|
||||
"INSERT INTO sessions (account_id, session_nr, session_id, session_token) \
|
||||
"INSERT INTO session (account_id, session_nr, session_id, session_token) \
|
||||
VALUES ($1, COALESCE((SELECT MAX(session_nr) + 1 \
|
||||
FROM sessions \
|
||||
FROM session \
|
||||
WHERE account_id = $1), 1), $2, $3);",
|
||||
&[&account_id, &session_id, &token],
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
Ok(Output { session_id, token })
|
||||
Ok(Output { session_id: session_id.to_string(), token })
|
||||
}
|
||||
|
@ -6,12 +6,14 @@ use serde_json::{from_value, to_value, Value};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
struct Input {
|
||||
room_id: String,
|
||||
room_id: Uuid,
|
||||
events: Vec<Event>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
struct Output {}
|
||||
struct Output {
|
||||
events: Vec<Uuid>,
|
||||
}
|
||||
|
||||
pub async fn handle(input: &InputEnvelope, session: Option<Session>) -> Result<Value, Error> {
|
||||
Ok(to_value(
|
||||
@ -21,9 +23,13 @@ pub async fn handle(input: &InputEnvelope, session: Option<Session>) -> Result<V
|
||||
|
||||
async fn new_event(input: Input, session: Option<Session>) -> Result<Output, Error> {
|
||||
let _account = get_account(&session)?;
|
||||
let mut uuids = vec![];
|
||||
// TODO check permissions
|
||||
for event in input.events {
|
||||
subscription::push(input.room_id.as_str(), event).await?;
|
||||
for mut event in input.events {
|
||||
let uuid = Uuid::new_v4();
|
||||
event.id = Some(uuid);
|
||||
uuids.push(uuid);
|
||||
subscription::push(&input.room_id, event).await?;
|
||||
}
|
||||
Ok(Output {})
|
||||
Ok(Output {events: uuids})
|
||||
}
|
||||
|
@ -10,12 +10,13 @@ use crypto::digest::Digest;
|
||||
use crypto::sha2::Sha256;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct InputEnvelope {
|
||||
pub endpoint: String,
|
||||
pub from_domain: Option<String>,
|
||||
pub to_domain: String,
|
||||
pub from_domain: Option<Uuid>,
|
||||
pub to_domain: Uuid,
|
||||
pub token: Option<String>,
|
||||
pub request_nr: Option<u64>,
|
||||
pub data: Value,
|
||||
@ -30,32 +31,21 @@ pub struct OutputEnvelope {
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct Event {
|
||||
data: Value,
|
||||
id: Option<Uuid>,
|
||||
}
|
||||
|
||||
pub struct Account {
|
||||
id: String,
|
||||
id: Uuid,
|
||||
name: String,
|
||||
domain: String,
|
||||
domain: Uuid,
|
||||
}
|
||||
|
||||
pub struct Session {
|
||||
id: String,
|
||||
id: Uuid,
|
||||
nr: i32,
|
||||
account: Option<Account>,
|
||||
}
|
||||
|
||||
pub fn get_id(input: &[&str]) -> String {
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.input_str(chrono::Utc::now().timestamp_millis().to_string().as_str());
|
||||
for part in input {
|
||||
hasher.input_str(" ");
|
||||
hasher.input_str(part);
|
||||
}
|
||||
let mut result = [0u8; 32];
|
||||
hasher.result(&mut result);
|
||||
base64_url::encode(&result)
|
||||
}
|
||||
|
||||
pub fn get_account(session: &Option<Session>) -> Result<&Account, Error> {
|
||||
match session {
|
||||
Some(session) => match &session.account {
|
||||
@ -97,8 +87,8 @@ impl Session {
|
||||
let res = client
|
||||
.query(
|
||||
"SELECT session_id, session_nr, a.account_id, account_name, domain_id \
|
||||
FROM accounts a JOIN sessions s ON a.account_id = s.account_id \
|
||||
WHERE session_token = $1;",
|
||||
FROM account a JOIN session s ON a.account_id = s.account_id \
|
||||
WHERE session_token = $1;",
|
||||
&[&token],
|
||||
)
|
||||
.await?;
|
||||
|
@ -4,8 +4,8 @@ use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::{mpsc, Mutex};
|
||||
|
||||
static mut ROOMS: Option<Arc<Mutex<HashMap<String, Vec<mpsc::Sender<Event>>>>>> = None;
|
||||
static mut ACCOUNTS: Option<Arc<Mutex<HashMap<String, Vec<mpsc::Sender<Event>>>>>> = None;
|
||||
static mut ROOMS: Option<Arc<Mutex<HashMap<Uuid, Vec<mpsc::Sender<Event>>>>>> = None;
|
||||
static mut ACCOUNTS: Option<Arc<Mutex<HashMap<Uuid, Vec<mpsc::Sender<Event>>>>>> = None;
|
||||
|
||||
pub fn init() {
|
||||
unsafe {
|
||||
@ -18,31 +18,31 @@ pub async fn subscribe_account(account: &Account) -> mpsc::Receiver<Event> {
|
||||
let (tx, rx) = mpsc::channel::<Event>(64);
|
||||
unsafe {
|
||||
let mut acc = ACCOUNTS.as_ref().unwrap().lock().await;
|
||||
match acc.get_mut(account.id.as_str()) {
|
||||
match acc.get_mut(&account.id) {
|
||||
Some(vec) => {
|
||||
vec.push(tx);
|
||||
}
|
||||
None => {
|
||||
acc.insert(account.id.clone(), vec![tx]);
|
||||
acc.insert(account.id, vec![tx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
rx
|
||||
}
|
||||
|
||||
pub async fn push(room_id: &str, event: Event) -> Result<(), Error> {
|
||||
pub async fn push(room_id: &Uuid, event: Event) -> Result<(), Error> {
|
||||
let backend = database::client().await?;
|
||||
let accounts = match backend {
|
||||
database::Client::Postgres(client) => {
|
||||
let res = client
|
||||
.query(
|
||||
"SELECT account_id \
|
||||
FROM members \
|
||||
WHERE room_id = $1;",
|
||||
FROM member \
|
||||
WHERE room_id = $1;",
|
||||
&[&room_id],
|
||||
)
|
||||
.await?;
|
||||
let mut acc: Vec<String> = Vec::new();
|
||||
let mut acc: Vec<Uuid> = Vec::new();
|
||||
for row in res {
|
||||
acc.push(row.get(0));
|
||||
}
|
||||
@ -62,7 +62,7 @@ pub async fn push(room_id: &str, event: Event) -> Result<(), Error> {
|
||||
for account in accounts {
|
||||
unsafe {
|
||||
let mut accounts = ACCOUNTS.as_ref().unwrap().lock().await;
|
||||
if let Some(acc) = accounts.get_mut(account.as_str()) {
|
||||
if let Some(acc) = accounts.get_mut(&account) {
|
||||
let mut acc = acc.clone();
|
||||
rooms.append(&mut acc);
|
||||
}
|
||||
|
Reference in New Issue
Block a user