feat(relays): layed the foundations for the relay runtime manager

This commit is contained in:
antifallobst 2024-03-24 19:40:52 +01:00
parent 4acdee0c18
commit 1d1f4896a4
Signed by: antifallobst
GPG Key ID: 2B4F402172791BAF
5 changed files with 87 additions and 6 deletions

View File

@ -6,9 +6,10 @@ use log::info;
mod endpoints; mod endpoints;
pub async fn start(config: &Config, backend: Backend) -> Result<()> { pub async fn start(config: &Config, backend: Backend) -> Result<()> {
let data = web::Data::new(backend);
let server = HttpServer::new(move || { let server = HttpServer::new(move || {
App::new() App::new()
.app_data(web::Data::new(backend.clone())) .app_data(data.clone())
.service(endpoints::account::register) .service(endpoints::account::register)
.service(endpoints::account::auth) .service(endpoints::account::auth)
.service(endpoints::account::invite::new) .service(endpoints::account::invite::new)

View File

@ -16,3 +16,15 @@ pub struct UsersRow {
pub password: String, pub password: String,
pub permissions: u16, pub permissions: u16,
} }
pub struct RelaysRow {
pub id: String,
pub secret: String,
}
pub struct RelayIndexRow {
pub public_id: String,
pub public_key: [u8; 32],
pub auth: String,
pub name: String,
}

View File

@ -13,6 +13,9 @@ pub enum Error {
#[error("Permission denied: {0}")] #[error("Permission denied: {0}")]
PermissionDenied(&'static str), PermissionDenied(&'static str),
#[error("The given relay id cannot be mapped to a relay")]
RelayNotFound,
#[error("The given token is expired")] #[error("The given token is expired")]
TokenExpired, TokenExpired,
@ -37,6 +40,7 @@ impl Into<HttpResponse> for Error {
Error::AuthenticationFailure => HttpResponse::Unauthorized().json(body), Error::AuthenticationFailure => HttpResponse::Unauthorized().json(body),
Error::InvalidToken => HttpResponse::Unauthorized().json(body), Error::InvalidToken => HttpResponse::Unauthorized().json(body),
Error::PermissionDenied(_) => HttpResponse::Forbidden().json(body), Error::PermissionDenied(_) => HttpResponse::Forbidden().json(body),
Error::RelayNotFound => HttpResponse::NotFound().json(body),
Error::TokenExpired => HttpResponse::Gone().json(body), Error::TokenExpired => HttpResponse::Gone().json(body),
Error::UserNotFound => HttpResponse::NotFound().json(body), Error::UserNotFound => HttpResponse::NotFound().json(body),
} }

View File

@ -5,14 +5,17 @@ mod relay;
mod tokens; mod tokens;
mod user; mod user;
use crate::config::Config; use crate::{backend::relay::Relay, config::Config};
use anyhow::Result; use anyhow::Result;
use log::info; use log::info;
use sqlx::MySqlPool; use sqlx::MySqlPool;
use std::collections::HashMap;
use uuid::Uuid;
#[derive(Debug, Clone)] #[derive(Debug)]
pub struct Backend { pub struct Backend {
pub pool: MySqlPool, pub pool: MySqlPool,
pub relays: HashMap<Uuid, Relay>,
} }
impl Backend { impl Backend {
@ -59,6 +62,9 @@ impl Backend {
info!("Backend initialized"); info!("Backend initialized");
Ok(Self { pool }) Ok(Self {
pool,
relays: HashMap::new(),
})
} }
} }

View File

@ -1,11 +1,37 @@
use crate::backend::{error::Error, user::IntoUser, Backend}; use crate::backend::{
db_structures::{RelayIndexRow, RelaysRow},
error::Error,
user::IntoUser,
Backend,
};
use anyhow::{bail, Result};
use argon2::{ use argon2::{
password_hash::{rand_core::OsRng, PasswordHasher, SaltString}, password_hash::{rand_core::OsRng, PasswordHasher, SaltString},
Argon2, Argon2,
}; };
use rand::distributions::DistString; use rand::distributions::DistString;
use std::collections::{HashMap, VecDeque};
use uuid::Uuid; use uuid::Uuid;
#[derive(Debug)]
struct EncryptedMessage {
sender: Uuid,
body: Vec<u8>,
}
#[derive(Debug)]
struct UserIndex {
public_key: [u8; 32],
name: String,
messages: VecDeque<EncryptedMessage>,
}
#[derive(Debug)]
pub struct Relay {
user_index: HashMap<Uuid, UserIndex>,
user_map: HashMap<Uuid, Uuid>,
}
impl Backend { impl Backend {
/// Creates the structures for a new relay /// Creates the structures for a new relay
pub async fn create_relay( pub async fn create_relay(
@ -38,7 +64,7 @@ impl Backend {
sqlx::query(&format!( sqlx::query(&format!(
"CREATE TABLE RelayIndex_{} ( "CREATE TABLE RelayIndex_{} (
public_id UUID NOT NULL PRIMARY KEY, public_id UUID NOT NULL PRIMARY KEY,
public_key BLOB(4096) NOT NULL, public_key BINARY(32) NOT NULL,
auth VARCHAR(128) NOT NULL, auth VARCHAR(128) NOT NULL,
name VARCHAR(32) name VARCHAR(32)
);", );",
@ -49,4 +75,36 @@ impl Backend {
Ok(Ok((relay_id, secret))) Ok(Ok((relay_id, secret)))
} }
pub async fn get_relay(&mut self, relay_id: Uuid) -> Result<Result<&Relay, Error>> {
// can't use early return pattern, because of the borrow checker :/
if self.relays.get(&relay_id).is_none() {
match sqlx::query_as!(
RelaysRow,
r#"SELECT * FROM Relays WHERE id = ? LIMIT 1;"#,
relay_id.as_bytes().as_slice()
)
.fetch_one(&self.pool)
.await
{
Err(sqlx::Error::RowNotFound) => return Ok(Err(Error::RelayNotFound)),
Err(e) => return Err(e.into()),
Ok(_) => {
// the hashmaps will be filled, as soon, as clients subscribe to the relay
let relay = Relay {
user_index: HashMap::new(),
user_map: HashMap::new(),
};
self.relays.insert(relay_id, relay);
}
}
}
if let Some(relay) = self.relays.get(&relay_id) {
Ok(Ok(relay))
} else {
bail!("!!! This should never happen !!! relay not found in runtime manager, even tho it was there before.")
}
}
} }