MTP migration
This commit is contained in:
parent
607bf337fe
commit
3e7b5121d4
7 changed files with 123 additions and 425 deletions
17
Cargo.lock
generated
17
Cargo.lock
generated
|
|
@ -2280,7 +2280,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mtp"
|
name = "mtp"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://git.methanium.net/methanium/mtp#8dc8b54b26544554f98d7af32200d6bf9a01d300"
|
source = "git+https://git.methanium.net/methanium/mtp#44ff1d8781b4645dbff3dce2406155e4d9d43a4c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"mtp-client",
|
"mtp-client",
|
||||||
"mtp-codec",
|
"mtp-codec",
|
||||||
|
|
@ -2294,7 +2294,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mtp-client"
|
name = "mtp-client"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://git.methanium.net/methanium/mtp#8dc8b54b26544554f98d7af32200d6bf9a01d300"
|
source = "git+https://git.methanium.net/methanium/mtp#44ff1d8781b4645dbff3dce2406155e4d9d43a4c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"mtp-codec",
|
"mtp-codec",
|
||||||
"mtp-common",
|
"mtp-common",
|
||||||
|
|
@ -2307,7 +2307,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mtp-codec"
|
name = "mtp-codec"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://git.methanium.net/methanium/mtp#8dc8b54b26544554f98d7af32200d6bf9a01d300"
|
source = "git+https://git.methanium.net/methanium/mtp#44ff1d8781b4645dbff3dce2406155e4d9d43a4c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64",
|
"base64",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
|
|
@ -2320,7 +2320,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mtp-common"
|
name = "mtp-common"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://git.methanium.net/methanium/mtp#8dc8b54b26544554f98d7af32200d6bf9a01d300"
|
source = "git+https://git.methanium.net/methanium/mtp#44ff1d8781b4645dbff3dce2406155e4d9d43a4c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quinn",
|
"quinn",
|
||||||
"rustls",
|
"rustls",
|
||||||
|
|
@ -2331,8 +2331,9 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mtp-crypto"
|
name = "mtp-crypto"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://git.methanium.net/methanium/mtp#8dc8b54b26544554f98d7af32200d6bf9a01d300"
|
source = "git+https://git.methanium.net/methanium/mtp#44ff1d8781b4645dbff3dce2406155e4d9d43a4c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"base64",
|
||||||
"chacha20poly1305",
|
"chacha20poly1305",
|
||||||
"ed25519-dalek",
|
"ed25519-dalek",
|
||||||
"getrandom 0.4.3",
|
"getrandom 0.4.3",
|
||||||
|
|
@ -2349,7 +2350,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mtp-host"
|
name = "mtp-host"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://git.methanium.net/methanium/mtp#8dc8b54b26544554f98d7af32200d6bf9a01d300"
|
source = "git+https://git.methanium.net/methanium/mtp#44ff1d8781b4645dbff3dce2406155e4d9d43a4c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"mtp-codec",
|
"mtp-codec",
|
||||||
"mtp-common",
|
"mtp-common",
|
||||||
|
|
@ -2362,7 +2363,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mtp-transport"
|
name = "mtp-transport"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://git.methanium.net/methanium/mtp#8dc8b54b26544554f98d7af32200d6bf9a01d300"
|
source = "git+https://git.methanium.net/methanium/mtp#44ff1d8781b4645dbff3dce2406155e4d9d43a4c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"mtp-codec",
|
"mtp-codec",
|
||||||
|
|
@ -2376,7 +2377,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mtp-type-map"
|
name = "mtp-type-map"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://git.methanium.net/methanium/mtp#8dc8b54b26544554f98d7af32200d6bf9a01d300"
|
source = "git+https://git.methanium.net/methanium/mtp#44ff1d8781b4645dbff3dce2406155e4d9d43a4c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_yaml",
|
"serde_yaml",
|
||||||
|
|
|
||||||
10
src/main.rs
10
src/main.rs
|
|
@ -1,10 +1,8 @@
|
||||||
mod notifications;
|
|
||||||
mod server;
|
mod server;
|
||||||
mod sql;
|
mod sql;
|
||||||
mod transport;
|
mod transport;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
use crate::notifications::tauri;
|
|
||||||
use crate::sql::sql::initialize_db;
|
use crate::sql::sql::initialize_db;
|
||||||
use crate::sql::sql::print_users;
|
use crate::sql::sql::print_users;
|
||||||
use crate::transport::omikron_connection;
|
use crate::transport::omikron_connection;
|
||||||
|
|
@ -53,13 +51,6 @@ async fn main() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let tauri_handle = tokio::spawn(async move {
|
|
||||||
match tauri::start(9189).await {
|
|
||||||
Err(e) => log_err!(0, PrintType::General, "{:?}", e),
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
log!("Started");
|
log!("Started");
|
||||||
log!(" .env");
|
log!(" .env");
|
||||||
if let Err(e) = initialize_db().await {
|
if let Err(e) = initialize_db().await {
|
||||||
|
|
@ -94,5 +85,4 @@ async fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
omikron_handle.abort();
|
omikron_handle.abort();
|
||||||
tauri_handle.abort();
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
pub mod tauri;
|
|
||||||
|
|
@ -1,157 +0,0 @@
|
||||||
use crate::log;
|
|
||||||
use crate::util::file_util::load_file_vec;
|
|
||||||
use crate::util::logger::PrintType;
|
|
||||||
use dashmap::DashMap;
|
|
||||||
use mtp::codec::{CommunicationType, CommunicationValue, DataType, DataValue};
|
|
||||||
use mtp::transport::{Host, Policy, Receiver, SendMode, Sender, host};
|
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
use std::net::{IpAddr, Ipv4Addr};
|
|
||||||
use std::sync::Arc;
|
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
pub struct TauriConnection {
|
|
||||||
pub user_id: i64,
|
|
||||||
pub sender: Arc<Sender>,
|
|
||||||
}
|
|
||||||
|
|
||||||
static TAURI_CONNECTIONS: Lazy<DashMap<i64, Vec<Arc<TauriConnection>>>> = Lazy::new(DashMap::new);
|
|
||||||
|
|
||||||
pub async fn start(port: u16) -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let cert_pem = load_file_vec("certs", "transport_cert.pem").expect("Error loading Pemfile");
|
|
||||||
let key_pem = load_file_vec("certs", "transport_key.pem").expect("Error loading Keyfile");
|
|
||||||
|
|
||||||
let mut host: Host = host(
|
|
||||||
IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
|
|
||||||
port,
|
|
||||||
cert_pem,
|
|
||||||
key_pem,
|
|
||||||
Policy {
|
|
||||||
send_mode: SendMode::SingleStreamPerMessage,
|
|
||||||
max_message_size: 1_000_000_000,
|
|
||||||
close_frame_len: u32::MAX,
|
|
||||||
application_close_code: 0,
|
|
||||||
open_stream_timeout: Duration::from_millis(2_000),
|
|
||||||
write_timeout: Duration::from_millis(2_000),
|
|
||||||
accept_stream_timeout: Duration::from_millis(10_000),
|
|
||||||
read_timeout: Duration::from_millis(30_000),
|
|
||||||
keep_alive_interval: Some(Duration::from_secs(6)),
|
|
||||||
max_idle_timeout: Some(Duration::from_secs(30)),
|
|
||||||
force_close_delay: Duration::from_millis(300),
|
|
||||||
max_transient_recv_errors: 20,
|
|
||||||
transient_recv_backoff: Duration::from_millis(100),
|
|
||||||
receiver_queue_capacity: 1000,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
log!("TauriServer listening on port {}", port);
|
|
||||||
|
|
||||||
while let Some((sender, mut receiver)) = host.next().await {
|
|
||||||
tokio::spawn(async move {
|
|
||||||
handle_connection(sender, &mut receiver).await;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn handle_connection(sender: Sender, receiver: &mut Receiver) {
|
|
||||||
let mut current_user_id = 0;
|
|
||||||
let sender = Arc::new(sender);
|
|
||||||
|
|
||||||
while let Ok(cv) = receiver.receive().await {
|
|
||||||
if cv.is_type(CommunicationType::TauriIdentification) {
|
|
||||||
let user_id = cv.get_data(DataType::UserId).as_number().unwrap_or(0) as i64;
|
|
||||||
if user_id != 0 {
|
|
||||||
current_user_id = user_id;
|
|
||||||
let conn = Arc::new(TauriConnection {
|
|
||||||
user_id,
|
|
||||||
sender: sender.clone(),
|
|
||||||
});
|
|
||||||
|
|
||||||
TAURI_CONNECTIONS
|
|
||||||
.entry(user_id)
|
|
||||||
.and_modify(|conns| {
|
|
||||||
conns.retain(|c| !Arc::ptr_eq(&c.sender.handle(), &sender.handle()));
|
|
||||||
conns.push(conn.clone());
|
|
||||||
})
|
|
||||||
.or_insert_with(|| vec![conn]);
|
|
||||||
|
|
||||||
let response =
|
|
||||||
CommunicationValue::new(CommunicationType::Success).with_id(cv.get_id());
|
|
||||||
if let Err(e) = sender.send(&response).await {
|
|
||||||
log!(
|
|
||||||
PrintType::General,
|
|
||||||
"Failed to send tauri success response: {}",
|
|
||||||
e
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
log!(user_id, PrintType::Client, "Tauri device registered");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if cv.is_type(CommunicationType::Ping) {
|
|
||||||
let response =
|
|
||||||
CommunicationValue::new(CommunicationType::Pong).with_id(cv.get_id());
|
|
||||||
if let Err(_) = sender.send(&response).await {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cleanup
|
|
||||||
if current_user_id != 0 {
|
|
||||||
if let Some(mut conns) = TAURI_CONNECTIONS.get_mut(¤t_user_id) {
|
|
||||||
conns.retain(|c| !Arc::ptr_eq(c.sender.handle(), sender.handle()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn send_notification(user_id: i64, sender_id: i64) {
|
|
||||||
let conns_opt = {
|
|
||||||
let entry = TAURI_CONNECTIONS.get(&user_id);
|
|
||||||
entry.map(|e| e.clone())
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(conns) = conns_opt {
|
|
||||||
let cv = CommunicationValue::new(CommunicationType::PushNotification)
|
|
||||||
.add_typed_default(DataType::SenderId, DataValue::SignedNumber(sender_id as i128));
|
|
||||||
|
|
||||||
let mut remove_needed = false;
|
|
||||||
for conn in conns.iter() {
|
|
||||||
if conn.sender.send(&cv).await.is_err() {
|
|
||||||
remove_needed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if remove_needed {
|
|
||||||
if let Some(mut conns_mut) = TAURI_CONNECTIONS.get_mut(&user_id) {
|
|
||||||
conns_mut.retain(|conn| conn.sender.is_open());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn remove_notification(user_id: i64, sender_id: i64) {
|
|
||||||
let conns_opt = {
|
|
||||||
let entry = TAURI_CONNECTIONS.get(&user_id);
|
|
||||||
entry.map(|e| e.clone())
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(conns) = conns_opt {
|
|
||||||
let cv = CommunicationValue::new(CommunicationType::ReadNotification)
|
|
||||||
.add_typed_default(DataType::SenderId, DataValue::SignedNumber(sender_id as i128));
|
|
||||||
|
|
||||||
let mut remove_needed = false;
|
|
||||||
for conn in conns.iter() {
|
|
||||||
if conn.sender.send(&cv).await.is_err() {
|
|
||||||
remove_needed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if remove_needed {
|
|
||||||
if let Some(mut conns_mut) = TAURI_CONNECTIONS.get_mut(&user_id) {
|
|
||||||
conns_mut.retain(|conn| conn.sender.is_open());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -68,7 +68,7 @@ pub async fn handle(path: &str, body_string: Option<String>) -> HttpResponse {
|
||||||
let mut res = JsonValue::new_object();
|
let mut res = JsonValue::new_object();
|
||||||
res["status"] = "success".into();
|
res["status"] = "success".into();
|
||||||
res["id"] = id.into();
|
res["id"] = id.into();
|
||||||
res["public_key"] = public_key.into();
|
res["public_key"] = public_key.to_base64().into();
|
||||||
res["ip_address"] = ip_address.into();
|
res["ip_address"] = ip_address.into();
|
||||||
|
|
||||||
(StatusCode::OK, res.dump())
|
(StatusCode::OK, res.dump())
|
||||||
|
|
@ -103,7 +103,7 @@ pub async fn handle(path: &str, body_string: Option<String>) -> HttpResponse {
|
||||||
let mut res = JsonValue::new_object();
|
let mut res = JsonValue::new_object();
|
||||||
res["status"] = "success".into();
|
res["status"] = "success".into();
|
||||||
res["id"] = id.into();
|
res["id"] = id.into();
|
||||||
res["public_key"] = public_key.into();
|
res["public_key"] = public_key.to_base64().into();
|
||||||
res["ip_address"] = ip_address.into();
|
res["ip_address"] = ip_address.into();
|
||||||
(StatusCode::OK, res.dump())
|
(StatusCode::OK, res.dump())
|
||||||
} else if let Some(omikron_id) = get_iota_primary_omikron_connection(id) {
|
} else if let Some(omikron_id) = get_iota_primary_omikron_connection(id) {
|
||||||
|
|
@ -111,7 +111,7 @@ pub async fn handle(path: &str, body_string: Option<String>) -> HttpResponse {
|
||||||
let mut res = JsonValue::new_object();
|
let mut res = JsonValue::new_object();
|
||||||
res["status"] = "success".into();
|
res["status"] = "success".into();
|
||||||
res["id"] = omikron_id.into();
|
res["id"] = omikron_id.into();
|
||||||
res["public_key"] = public_key.into();
|
res["public_key"] = public_key.to_base64().into();
|
||||||
res["ip_address"] = ip_address.into();
|
res["ip_address"] = ip_address.into();
|
||||||
(StatusCode::OK, res.dump())
|
(StatusCode::OK, res.dump())
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -126,7 +126,7 @@ pub async fn handle(path: &str, body_string: Option<String>) -> HttpResponse {
|
||||||
let mut res = JsonValue::new_object();
|
let mut res = JsonValue::new_object();
|
||||||
res["status"] = "success".into();
|
res["status"] = "success".into();
|
||||||
res["id"] = omikron_id.into();
|
res["id"] = omikron_id.into();
|
||||||
res["public_key"] = public_key.into();
|
res["public_key"] = public_key.to_base64().into();
|
||||||
res["ip_address"] = ip_address.into();
|
res["ip_address"] = ip_address.into();
|
||||||
(StatusCode::OK, res.dump())
|
(StatusCode::OK, res.dump())
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -172,7 +172,7 @@ pub async fn handle(path: &str, body_string: Option<String>) -> HttpResponse {
|
||||||
let mut res = JsonValue::new_object();
|
let mut res = JsonValue::new_object();
|
||||||
res["status"] = "success".into();
|
res["status"] = "success".into();
|
||||||
res["username"] = username.into();
|
res["username"] = username.into();
|
||||||
res["public_key"] = public_key.into();
|
res["public_key"] = public_key.to_base64().into();
|
||||||
res["user_id"] = id.into();
|
res["user_id"] = id.into();
|
||||||
res["iota_id"] = iota_id.into();
|
res["iota_id"] = iota_id.into();
|
||||||
res["sub_level"] = sub_level.into();
|
res["sub_level"] = sub_level.into();
|
||||||
|
|
@ -227,7 +227,7 @@ pub async fn handle(path: &str, body_string: Option<String>) -> HttpResponse {
|
||||||
let mut res = JsonValue::new_object();
|
let mut res = JsonValue::new_object();
|
||||||
res["status"] = "success".into();
|
res["status"] = "success".into();
|
||||||
res["username"] = username.into();
|
res["username"] = username.into();
|
||||||
res["public_key"] = public_key.into();
|
res["public_key"] = public_key.to_base64().into();
|
||||||
res["user_id"] = id.into();
|
res["user_id"] = id.into();
|
||||||
res["iota_id"] = iota_id.into();
|
res["iota_id"] = iota_id.into();
|
||||||
res["sub_level"] = sub_level.into();
|
res["sub_level"] = sub_level.into();
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::log;
|
use crate::log;
|
||||||
|
use mtp_crypto::PublicKeyBundle;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use sqlx::{MySql, Pool, Row, mysql::MySqlPoolOptions};
|
use sqlx::{MySql, Pool, Row, mysql::MySqlPoolOptions};
|
||||||
use std::sync::atomic::{AtomicU64, Ordering};
|
use std::sync::atomic::{AtomicU64, Ordering};
|
||||||
|
|
@ -61,7 +62,7 @@ pub async fn initialize_db() -> Result<(), sqlx::Error> {
|
||||||
avatar MEDIUMBLOB,
|
avatar MEDIUMBLOB,
|
||||||
sub_level INT(11) NOT NULL DEFAULT 0,
|
sub_level INT(11) NOT NULL DEFAULT 0,
|
||||||
sub_end BIGINT(20) NOT NULL DEFAULT 0,
|
sub_end BIGINT(20) NOT NULL DEFAULT 0,
|
||||||
public_key TEXT NOT NULL COLLATE utf8mb4_bin,
|
public_key BLOB NOT NULL,
|
||||||
private_key_hash TEXT NOT NULL COLLATE utf8mb4_bin DEFAULT '',
|
private_key_hash TEXT NOT NULL COLLATE utf8mb4_bin DEFAULT '',
|
||||||
iota_id BIGINT NOT NULL,
|
iota_id BIGINT NOT NULL,
|
||||||
token VARCHAR(256) NOT NULL UNIQUE COLLATE utf8mb4_bin
|
token VARCHAR(256) NOT NULL UNIQUE COLLATE utf8mb4_bin
|
||||||
|
|
@ -73,7 +74,7 @@ pub async fn initialize_db() -> Result<(), sqlx::Error> {
|
||||||
"CREATE TABLE IF NOT EXISTS
|
"CREATE TABLE IF NOT EXISTS
|
||||||
iotas (
|
iotas (
|
||||||
id BIGINT NOT NULL PRIMARY KEY,
|
id BIGINT NOT NULL PRIMARY KEY,
|
||||||
public_key VARCHAR(255) NOT NULL COLLATE utf8mb4_bin
|
public_key BLOB NOT NULL
|
||||||
)",
|
)",
|
||||||
)
|
)
|
||||||
.execute(&pool)
|
.execute(&pool)
|
||||||
|
|
@ -82,7 +83,7 @@ pub async fn initialize_db() -> Result<(), sqlx::Error> {
|
||||||
"CREATE TABLE IF NOT EXISTS
|
"CREATE TABLE IF NOT EXISTS
|
||||||
omikrons (
|
omikrons (
|
||||||
id BIGINT NOT NULL PRIMARY KEY,
|
id BIGINT NOT NULL PRIMARY KEY,
|
||||||
public_key VARCHAR(255) NOT NULL COLLATE utf8mb4_bin,
|
public_key BLOB NOT NULL,
|
||||||
location VARCHAR(255) NOT NULL COLLATE utf8mb4_bin,
|
location VARCHAR(255) NOT NULL COLLATE utf8mb4_bin,
|
||||||
ip_address VARCHAR(255) NOT NULL COLLATE utf8mb4_bin
|
ip_address VARCHAR(255) NOT NULL COLLATE utf8mb4_bin
|
||||||
)",
|
)",
|
||||||
|
|
@ -160,7 +161,7 @@ pub async fn get_by_username(
|
||||||
Option<Vec<u8>>,
|
Option<Vec<u8>>,
|
||||||
i32,
|
i32,
|
||||||
i64,
|
i64,
|
||||||
String,
|
PublicKeyBundle,
|
||||||
String,
|
String,
|
||||||
String,
|
String,
|
||||||
),
|
),
|
||||||
|
|
@ -192,7 +193,8 @@ pub async fn get_by_username(
|
||||||
let avatar: Option<Vec<u8>> = row.get("avatar");
|
let avatar: Option<Vec<u8>> = row.get("avatar");
|
||||||
let sub_level: i32 = row.get("sub_level");
|
let sub_level: i32 = row.get("sub_level");
|
||||||
let sub_end: i64 = row.get("sub_end");
|
let sub_end: i64 = row.get("sub_end");
|
||||||
let public_key: Vec<u8> = row.get("public_key");
|
let public_key = PublicKeyBundle::from_bytes(&row.get::<Vec<u8>, _>("public_key"))
|
||||||
|
.map_err(|e| sqlx::Error::Decode(Box::new(e)))?;
|
||||||
let private_key_hash: Vec<u8> = row.get("private_key_hash");
|
let private_key_hash: Vec<u8> = row.get("private_key_hash");
|
||||||
let token: Vec<u8> = row.get("token");
|
let token: Vec<u8> = row.get("token");
|
||||||
|
|
||||||
|
|
@ -206,7 +208,7 @@ pub async fn get_by_username(
|
||||||
avatar,
|
avatar,
|
||||||
sub_level,
|
sub_level,
|
||||||
sub_end,
|
sub_end,
|
||||||
String::from_utf8_lossy(&public_key).to_string(),
|
public_key,
|
||||||
String::from_utf8_lossy(&private_key_hash).to_string(),
|
String::from_utf8_lossy(&private_key_hash).to_string(),
|
||||||
String::from_utf8_lossy(&token).to_string(),
|
String::from_utf8_lossy(&token).to_string(),
|
||||||
))
|
))
|
||||||
|
|
@ -228,7 +230,7 @@ pub async fn get_by_user_id(
|
||||||
Option<Vec<u8>>,
|
Option<Vec<u8>>,
|
||||||
i32,
|
i32,
|
||||||
i64,
|
i64,
|
||||||
String,
|
PublicKeyBundle,
|
||||||
String,
|
String,
|
||||||
String,
|
String,
|
||||||
),
|
),
|
||||||
|
|
@ -260,7 +262,8 @@ pub async fn get_by_user_id(
|
||||||
let avatar: Option<Vec<u8>> = row.get("avatar");
|
let avatar: Option<Vec<u8>> = row.get("avatar");
|
||||||
let sub_level: i32 = row.get("sub_level");
|
let sub_level: i32 = row.get("sub_level");
|
||||||
let sub_end: i64 = row.get("sub_end");
|
let sub_end: i64 = row.get("sub_end");
|
||||||
let public_key: Vec<u8> = row.get("public_key");
|
let public_key = PublicKeyBundle::from_bytes(&row.get::<Vec<u8>, _>("public_key"))
|
||||||
|
.map_err(|e| sqlx::Error::Decode(Box::new(e)))?;
|
||||||
let private_key_hash: Vec<u8> = row.get("private_key_hash");
|
let private_key_hash: Vec<u8> = row.get("private_key_hash");
|
||||||
let token: Vec<u8> = row.get("token");
|
let token: Vec<u8> = row.get("token");
|
||||||
|
|
||||||
|
|
@ -274,7 +277,7 @@ pub async fn get_by_user_id(
|
||||||
avatar,
|
avatar,
|
||||||
sub_level,
|
sub_level,
|
||||||
sub_end,
|
sub_end,
|
||||||
String::from_utf8_lossy(&public_key).to_string(),
|
public_key,
|
||||||
String::from_utf8_lossy(&private_key_hash).to_string(),
|
String::from_utf8_lossy(&private_key_hash).to_string(),
|
||||||
String::from_utf8_lossy(&token).to_string(),
|
String::from_utf8_lossy(&token).to_string(),
|
||||||
))
|
))
|
||||||
|
|
@ -296,7 +299,7 @@ pub async fn get_users_by_iota_id(
|
||||||
Option<Vec<u8>>,
|
Option<Vec<u8>>,
|
||||||
i32,
|
i32,
|
||||||
i64,
|
i64,
|
||||||
String,
|
PublicKeyBundle,
|
||||||
String,
|
String,
|
||||||
String,
|
String,
|
||||||
)>,
|
)>,
|
||||||
|
|
@ -328,7 +331,8 @@ pub async fn get_users_by_iota_id(
|
||||||
let avatar: Option<Vec<u8>> = row.get("avatar");
|
let avatar: Option<Vec<u8>> = row.get("avatar");
|
||||||
let sub_level: i32 = row.get("sub_level");
|
let sub_level: i32 = row.get("sub_level");
|
||||||
let sub_end: i64 = row.get("sub_end");
|
let sub_end: i64 = row.get("sub_end");
|
||||||
let public_key: Vec<u8> = row.get("public_key");
|
let public_key = PublicKeyBundle::from_bytes(&row.get::<Vec<u8>, _>("public_key"))
|
||||||
|
.map_err(|e| sqlx::Error::Decode(Box::new(e)))?;
|
||||||
let private_key_hash: Vec<u8> = row.get("private_key_hash");
|
let private_key_hash: Vec<u8> = row.get("private_key_hash");
|
||||||
let token: Vec<u8> = row.get("token");
|
let token: Vec<u8> = row.get("token");
|
||||||
|
|
||||||
|
|
@ -342,7 +346,7 @@ pub async fn get_users_by_iota_id(
|
||||||
avatar,
|
avatar,
|
||||||
sub_level,
|
sub_level,
|
||||||
sub_end,
|
sub_end,
|
||||||
String::from_utf8_lossy(&public_key).to_string(),
|
public_key,
|
||||||
String::from_utf8_lossy(&private_key_hash).to_string(),
|
String::from_utf8_lossy(&private_key_hash).to_string(),
|
||||||
String::from_utf8_lossy(&token).to_string(),
|
String::from_utf8_lossy(&token).to_string(),
|
||||||
));
|
));
|
||||||
|
|
@ -477,7 +481,7 @@ pub async fn change_iota_id(id: i64, new_iota_id: i64) -> Result<(), sqlx::Error
|
||||||
|
|
||||||
pub async fn change_keys(
|
pub async fn change_keys(
|
||||||
id: i64,
|
id: i64,
|
||||||
new_public_key: String,
|
new_public_key: PublicKeyBundle,
|
||||||
new_private_key_hash: String,
|
new_private_key_hash: String,
|
||||||
) -> Result<(), sqlx::Error> {
|
) -> Result<(), sqlx::Error> {
|
||||||
let pool = {
|
let pool = {
|
||||||
|
|
@ -489,7 +493,7 @@ pub async fn change_keys(
|
||||||
};
|
};
|
||||||
|
|
||||||
sqlx::query("UPDATE users SET public_key = ?, private_key_hash = ? WHERE id = ?")
|
sqlx::query("UPDATE users SET public_key = ?, private_key_hash = ? WHERE id = ?")
|
||||||
.bind(new_public_key.as_bytes().to_vec())
|
.bind(new_public_key.as_bytes())
|
||||||
.bind(new_private_key_hash.as_bytes().to_vec())
|
.bind(new_private_key_hash.as_bytes().to_vec())
|
||||||
.bind(id)
|
.bind(id)
|
||||||
.execute(&pool)
|
.execute(&pool)
|
||||||
|
|
@ -517,7 +521,7 @@ pub async fn change_token(id: i64, new_token: String) -> Result<(), sqlx::Error>
|
||||||
pub async fn register_complete_user(
|
pub async fn register_complete_user(
|
||||||
id: i64,
|
id: i64,
|
||||||
username: String,
|
username: String,
|
||||||
public_key: String,
|
public_key: PublicKeyBundle,
|
||||||
iota_id: i64,
|
iota_id: i64,
|
||||||
token: String,
|
token: String,
|
||||||
) -> Result<(), sqlx::Error> {
|
) -> Result<(), sqlx::Error> {
|
||||||
|
|
@ -534,7 +538,7 @@ pub async fn register_complete_user(
|
||||||
)
|
)
|
||||||
.bind(id)
|
.bind(id)
|
||||||
.bind(username.as_bytes().to_vec())
|
.bind(username.as_bytes().to_vec())
|
||||||
.bind(public_key.as_bytes().to_vec())
|
.bind(public_key.as_bytes())
|
||||||
.bind(iota_id)
|
.bind(iota_id)
|
||||||
.bind(token.as_bytes().to_vec())
|
.bind(token.as_bytes().to_vec())
|
||||||
.execute(&pool)
|
.execute(&pool)
|
||||||
|
|
@ -589,13 +593,13 @@ pub async fn print_users() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
// ==========================================================================================
|
// ==========================================================================================
|
||||||
// IOTA
|
// IOTA
|
||||||
// ==========================================================================================
|
// ==========================================================================================
|
||||||
pub async fn create_new_iota(public_key: String) -> Result<i64, sqlx::Error> {
|
pub async fn create_new_iota(public_key: PublicKeyBundle) -> Result<i64, sqlx::Error> {
|
||||||
let new_id = get_register_id().await as i64;
|
let new_id = get_register_id().await as i64;
|
||||||
register_complete_iota(new_id, public_key).await?;
|
register_complete_iota(new_id, public_key).await?;
|
||||||
Ok(new_id)
|
Ok(new_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn register_complete_iota(id: i64, public_key: String) -> Result<(), sqlx::Error> {
|
pub async fn register_complete_iota(id: i64, public_key: PublicKeyBundle) -> Result<(), sqlx::Error> {
|
||||||
let pool = {
|
let pool = {
|
||||||
let db_lock = SQL_DB.read().await;
|
let db_lock = SQL_DB.read().await;
|
||||||
db_lock
|
db_lock
|
||||||
|
|
@ -606,14 +610,14 @@ pub async fn register_complete_iota(id: i64, public_key: String) -> Result<(), s
|
||||||
|
|
||||||
sqlx::query("INSERT INTO iotas (id, public_key) VALUES (?, ?)")
|
sqlx::query("INSERT INTO iotas (id, public_key) VALUES (?, ?)")
|
||||||
.bind(id)
|
.bind(id)
|
||||||
.bind(public_key.as_bytes().to_vec())
|
.bind(public_key.as_bytes())
|
||||||
.execute(&pool)
|
.execute(&pool)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_iota_by_id(id: i64) -> Result<(i64, String), sqlx::Error> {
|
pub async fn get_iota_by_id(id: i64) -> Result<(i64, PublicKeyBundle), sqlx::Error> {
|
||||||
let pool = {
|
let pool = {
|
||||||
let db_lock = SQL_DB.read().await;
|
let db_lock = SQL_DB.read().await;
|
||||||
db_lock
|
db_lock
|
||||||
|
|
@ -631,7 +635,9 @@ pub async fn get_iota_by_id(id: i64) -> Result<(i64, String), sqlx::Error> {
|
||||||
match result {
|
match result {
|
||||||
Ok(optional_row) => match optional_row {
|
Ok(optional_row) => match optional_row {
|
||||||
Some((id_i64, public_key)) => {
|
Some((id_i64, public_key)) => {
|
||||||
Ok((id_i64, String::from_utf8_lossy(&public_key).to_string()))
|
let bundle = PublicKeyBundle::from_bytes(&public_key)
|
||||||
|
.map_err(|e| sqlx::Error::Decode(Box::new(e)))?;
|
||||||
|
Ok((id_i64, bundle))
|
||||||
}
|
}
|
||||||
_ => Err(sqlx::Error::RowNotFound),
|
_ => Err(sqlx::Error::RowNotFound),
|
||||||
},
|
},
|
||||||
|
|
@ -639,7 +645,7 @@ pub async fn get_iota_by_id(id: i64) -> Result<(i64, String), sqlx::Error> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn change_iota_key(id: i64, new_key: String) -> Result<(), sqlx::Error> {
|
pub async fn change_iota_key(id: i64, new_key: PublicKeyBundle) -> Result<(), sqlx::Error> {
|
||||||
let pool = {
|
let pool = {
|
||||||
let db_lock = SQL_DB.read().await;
|
let db_lock = SQL_DB.read().await;
|
||||||
db_lock
|
db_lock
|
||||||
|
|
@ -649,7 +655,7 @@ pub async fn change_iota_key(id: i64, new_key: String) -> Result<(), sqlx::Error
|
||||||
};
|
};
|
||||||
|
|
||||||
sqlx::query("UPDATE iotas SET public_key = ? WHERE id = ?")
|
sqlx::query("UPDATE iotas SET public_key = ? WHERE id = ?")
|
||||||
.bind(new_key.as_bytes().to_vec())
|
.bind(new_key.as_bytes())
|
||||||
.bind(id)
|
.bind(id)
|
||||||
.execute(&pool)
|
.execute(&pool)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
@ -678,7 +684,7 @@ pub async fn delete_iota(id: i64) -> Result<(), sqlx::Error> {
|
||||||
// OMIKRONS
|
// OMIKRONS
|
||||||
// ==========================================================================================
|
// ==========================================================================================
|
||||||
|
|
||||||
pub async fn get_omikron_by_id(id: i64) -> Result<(String, String), sqlx::Error> {
|
pub async fn get_omikron_by_id(id: i64) -> Result<(PublicKeyBundle, String), sqlx::Error> {
|
||||||
let pool = {
|
let pool = {
|
||||||
let db_lock = SQL_DB.read().await;
|
let db_lock = SQL_DB.read().await;
|
||||||
db_lock
|
db_lock
|
||||||
|
|
@ -695,10 +701,11 @@ pub async fn get_omikron_by_id(id: i64) -> Result<(String, String), sqlx::Error>
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
match row {
|
match row {
|
||||||
Some((public_key, ip_address)) => Ok((
|
Some((public_key, ip_address)) => {
|
||||||
String::from_utf8_lossy(&public_key).to_string(),
|
let bundle = PublicKeyBundle::from_bytes(&public_key)
|
||||||
String::from_utf8_lossy(&ip_address).to_string(),
|
.map_err(|e| sqlx::Error::Decode(Box::new(e)))?;
|
||||||
)),
|
Ok((bundle, String::from_utf8_lossy(&ip_address).to_string()))
|
||||||
|
}
|
||||||
_ => Err(sqlx::Error::RowNotFound),
|
_ => Err(sqlx::Error::RowNotFound),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
get_keyring, get_public_key_bundle, log, log_cv_in, log_cv_out, log_err, log_in,
|
load_keyring, log, log_cv_in, log_cv_out, log_err, log_in,
|
||||||
server::short_link::add_short_link,
|
server::short_link::add_short_link,
|
||||||
sql::{
|
sql::{
|
||||||
connection_status::UserStatus,
|
connection_status::UserStatus,
|
||||||
sql::{self, get_by_user_id, get_by_username, get_iota_by_id, get_omikron_by_id},
|
sql::{self, get_by_user_id, get_by_username, get_iota_by_id},
|
||||||
user_online_tracker::{self},
|
user_online_tracker::{self},
|
||||||
},
|
},
|
||||||
transport::omikron_manager,
|
transport::omikron_manager,
|
||||||
|
|
@ -11,9 +11,12 @@ use crate::{
|
||||||
};
|
};
|
||||||
use base64::{Engine as _, engine::general_purpose::STANDARD};
|
use base64::{Engine as _, engine::general_purpose::STANDARD};
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
use mtp::{codec::{CommunicationType, CommunicationValue, DataType, DataValue}, host::{HostConfig, MTPHost}};
|
use mtp::host::{AuthenticationPolicy, Receiver, Sender};
|
||||||
use mtp::transport::{Host, Policy, Receiver, SendMode, Sender, host};
|
use mtp::{
|
||||||
use rand::{Rng, distributions::Alphanumeric};
|
codec::{CommunicationType, CommunicationValue, DataType, DataValue},
|
||||||
|
host::{Host, HostConfig, Policy, SendMode},
|
||||||
|
};
|
||||||
|
use mtp_crypto::PublicKeyBundle;
|
||||||
use std::net::{IpAddr, Ipv4Addr};
|
use std::net::{IpAddr, Ipv4Addr};
|
||||||
use std::{
|
use std::{
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
|
|
@ -23,8 +26,6 @@ use tokio::{
|
||||||
sync::{Mutex, RwLock},
|
sync::{Mutex, RwLock},
|
||||||
time::interval,
|
time::interval,
|
||||||
};
|
};
|
||||||
use x448::PublicKey;
|
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Configuration
|
// Configuration
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
@ -63,35 +64,6 @@ pub struct WaitingTask {
|
||||||
pub inserted_at: Instant,
|
pub inserted_at: Instant,
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Connection State
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
||||||
enum AuthState {
|
|
||||||
Unauthenticated,
|
|
||||||
Identified { omikron_id: i64 },
|
|
||||||
Authenticated { omikron_id: i64 },
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AuthState {
|
|
||||||
fn is_authenticated(&self) -> bool {
|
|
||||||
match self {
|
|
||||||
AuthState::Authenticated { omikron_id } => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn omikron_id(&self) -> Option<i64> {
|
|
||||||
match self {
|
|
||||||
AuthState::Identified { omikron_id } | AuthState::Authenticated { omikron_id } => {
|
|
||||||
Some(*omikron_id)
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Omikron Connection (mtp/QUIC-based)
|
// Omikron Connection (mtp/QUIC-based)
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
@ -99,7 +71,6 @@ impl AuthState {
|
||||||
pub struct OmikronConnection {
|
pub struct OmikronConnection {
|
||||||
id: u64,
|
id: u64,
|
||||||
sender: Mutex<Option<Sender>>,
|
sender: Mutex<Option<Sender>>,
|
||||||
state: RwLock<AuthState>,
|
|
||||||
challenge: RwLock<String>,
|
challenge: RwLock<String>,
|
||||||
pub_key: RwLock<Option<Vec<u8>>>,
|
pub_key: RwLock<Option<Vec<u8>>>,
|
||||||
pub ping: RwLock<i64>,
|
pub ping: RwLock<i64>,
|
||||||
|
|
@ -124,7 +95,6 @@ impl OmikronConnection {
|
||||||
let conn = Arc::new(Self {
|
let conn = Arc::new(Self {
|
||||||
id: rand::random(),
|
id: rand::random(),
|
||||||
sender: Mutex::new(Some(sender)),
|
sender: Mutex::new(Some(sender)),
|
||||||
state: RwLock::new(AuthState::Unauthenticated),
|
|
||||||
challenge: RwLock::new(String::new()),
|
challenge: RwLock::new(String::new()),
|
||||||
pub_key: RwLock::new(None),
|
pub_key: RwLock::new(None),
|
||||||
ping: RwLock::new(-1),
|
ping: RwLock::new(-1),
|
||||||
|
|
@ -197,143 +167,8 @@ impl OmikronConnection {
|
||||||
if cv.is_type(CommunicationType::Ping) {
|
if cv.is_type(CommunicationType::Ping) {
|
||||||
return self.handle_ping(cv).await;
|
return self.handle_ping(cv).await;
|
||||||
}
|
}
|
||||||
|
return Ok(());
|
||||||
let current_state = *self.state.read().await;
|
|
||||||
// Route based on authentication state
|
|
||||||
match current_state {
|
|
||||||
AuthState::Unauthenticated => self.clone().handle_unauthenticated(cv).await,
|
|
||||||
AuthState::Identified { .. } => self.clone().handle_identified(cv).await,
|
|
||||||
AuthState::Authenticated { omikron_id } => {
|
|
||||||
self.clone().handle_authenticated(cv, omikron_id).await
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
// Authentication Handlers
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
|
|
||||||
async fn handle_unauthenticated(self: Arc<Self>, cv: CommunicationValue) -> OmikronResult<()> {
|
|
||||||
if !cv.is_type(CommunicationType::Identification) {
|
|
||||||
let _ = self
|
|
||||||
.send_error_response(cv.get_id(), CommunicationType::ErrorNotAuthenticated)
|
|
||||||
.await;
|
|
||||||
return Err(OmikronError::NotAuthenticated);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract omikron ID
|
|
||||||
let omikron_id = cv
|
|
||||||
.get_data(DataType::OmikronId)
|
|
||||||
.as_number()
|
|
||||||
.ok_or(OmikronError::InvalidResponse)?;
|
|
||||||
log!("Omikron {:?} connected", omikron_id);
|
|
||||||
|
|
||||||
// Lookup omikron in database
|
|
||||||
let (public_key, _) = get_omikron_by_id(omikron_id as i64)
|
|
||||||
.await
|
|
||||||
.map_err(|e| OmikronError::Sql(e.to_string()))?;
|
|
||||||
|
|
||||||
log!("Got public Key");
|
|
||||||
|
|
||||||
let pub_key_bytes = STANDARD
|
|
||||||
.decode(&public_key)
|
|
||||||
.map_err(|_| OmikronError::AuthenticationFailed)?;
|
|
||||||
|
|
||||||
let pub_key_bytes_clone = pub_key_bytes.clone();
|
|
||||||
let omikron_pub_key = PublicKey::from_bytes(&pub_key_bytes_clone)
|
|
||||||
.ok_or(OmikronError::AuthenticationFailed)?;
|
|
||||||
|
|
||||||
log!("Decoded public Key");
|
|
||||||
|
|
||||||
// Generate challenge
|
|
||||||
let challenge: String = rand::thread_rng()
|
|
||||||
.sample_iter(&Alphanumeric)
|
|
||||||
.take(32)
|
|
||||||
.map(char::from)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
log!("Generated Challenge");
|
|
||||||
|
|
||||||
*self.challenge.write().await = challenge.clone();
|
|
||||||
|
|
||||||
log!("Stored Challenge");
|
|
||||||
|
|
||||||
*self.pub_key.write().await = Some(pub_key_bytes);
|
|
||||||
|
|
||||||
log!("Stored Pubkey");
|
|
||||||
|
|
||||||
*self.state.write().await = AuthState::Identified {
|
|
||||||
omikron_id: omikron_id as i64,
|
|
||||||
};
|
|
||||||
|
|
||||||
log!("Stored State");
|
|
||||||
|
|
||||||
let challenge_clone: String = challenge.clone();
|
|
||||||
let private_key = get_keyring();
|
|
||||||
let public_key_for_encrypt = omikron_pub_key;
|
|
||||||
|
|
||||||
let encrypted = tokio::task::spawn_blocking(move || {
|
|
||||||
challenge_clone
|
|
||||||
encrypt(private_key, public_key_for_encrypt, &challenge_clone)
|
|
||||||
.map_err(|_| OmikronError::AuthenticationFailed)
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.map_err(|_| OmikronError::AuthenticationFailed)??;
|
|
||||||
|
|
||||||
log!("Encrypted Challenge");
|
|
||||||
|
|
||||||
// Send challenge response
|
|
||||||
let response = CommunicationValue::new(CommunicationType::Challenge)
|
|
||||||
.with_id(cv.get_id())
|
|
||||||
.add_typed_default(
|
|
||||||
DataType::PublicKey,
|
|
||||||
DataValue::Str(STANDARD.encode(get_public_key_bundle().as_bytes())),
|
|
||||||
)
|
|
||||||
.add_typed_default(DataType::Content, DataValue::Str(encrypted));
|
|
||||||
|
|
||||||
log!("Sending Challenge");
|
|
||||||
self.send(&response).await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn handle_identified(self: Arc<Self>, cv: CommunicationValue) -> OmikronResult<()> {
|
|
||||||
if !cv.is_type(CommunicationType::ChallengeResponse) {
|
|
||||||
let _ = self
|
|
||||||
.send_error_response(cv.get_id(), CommunicationType::ErrorNotAuthenticated)
|
|
||||||
.await;
|
|
||||||
return Err(OmikronError::NotAuthenticated);
|
|
||||||
}
|
|
||||||
|
|
||||||
let client_response = cv
|
|
||||||
.get_data(DataType::Content)
|
|
||||||
.as_str()
|
|
||||||
.ok_or(OmikronError::InvalidResponse)?;
|
|
||||||
|
|
||||||
let expected_challenge = self.challenge.read().await.clone();
|
|
||||||
|
|
||||||
if client_response == expected_challenge {
|
|
||||||
let omikron_id = self.state.read().await.omikron_id().unwrap_or(0);
|
|
||||||
*self.state.write().await = AuthState::Authenticated { omikron_id };
|
|
||||||
|
|
||||||
omikron_manager::add_omikron(self.clone()).await;
|
|
||||||
|
|
||||||
let response = CommunicationValue::new(CommunicationType::IdentificationResponse)
|
|
||||||
.with_id(cv.get_id())
|
|
||||||
.add_typed_default(DataType::Accepted, DataValue::Bool(true));
|
|
||||||
|
|
||||||
self.clone().send(&response).await?;
|
|
||||||
log_in!(omikron_id, PrintType::Omega, "Omikron authenticated");
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
let _ = self
|
|
||||||
.send_error_response(cv.get_id(), CommunicationType::ErrorInvalidChallenge)
|
|
||||||
.await;
|
|
||||||
Err(OmikronError::AuthenticationFailed)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
// Authenticated Message Handlers
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
|
|
||||||
async fn handle_authenticated(
|
async fn handle_authenticated(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
|
|
@ -554,7 +389,7 @@ impl OmikronConnection {
|
||||||
Option<Vec<u8>>,
|
Option<Vec<u8>>,
|
||||||
i32,
|
i32,
|
||||||
i64,
|
i64,
|
||||||
String,
|
PublicKeyBundle,
|
||||||
String,
|
String,
|
||||||
String,
|
String,
|
||||||
),
|
),
|
||||||
|
|
@ -577,7 +412,7 @@ impl OmikronConnection {
|
||||||
let mut response = CommunicationValue::new(CommunicationType::GetUserData)
|
let mut response = CommunicationValue::new(CommunicationType::GetUserData)
|
||||||
.with_id(msg_id)
|
.with_id(msg_id)
|
||||||
.add_typed_default(DataType::Username, DataValue::Str(username.clone()))
|
.add_typed_default(DataType::Username, DataValue::Str(username.clone()))
|
||||||
.add_typed_default(DataType::PublicKey, DataValue::Str(public_key))
|
.add_typed_default(DataType::PublicKey, DataValue::Str(public_key.to_base64()))
|
||||||
.add_typed_default(DataType::UserId, DataValue::SignedNumber(id.into()))
|
.add_typed_default(DataType::UserId, DataValue::SignedNumber(id.into()))
|
||||||
.add_typed_default(DataType::IotaId, DataValue::SignedNumber(iota_id.into()))
|
.add_typed_default(DataType::IotaId, DataValue::SignedNumber(iota_id.into()))
|
||||||
.add_typed_default(
|
.add_typed_default(
|
||||||
|
|
@ -704,13 +539,13 @@ impl OmikronConnection {
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
msg_id: u32,
|
msg_id: u32,
|
||||||
iota_id: i64,
|
iota_id: i64,
|
||||||
public_key: String,
|
public_key: PublicKeyBundle,
|
||||||
user_id: Option<i64>,
|
user_id: Option<i64>,
|
||||||
username: Option<String>,
|
username: Option<String>,
|
||||||
) -> CommunicationValue {
|
) -> CommunicationValue {
|
||||||
let mut response = CommunicationValue::new(CommunicationType::GetIotaData)
|
let mut response = CommunicationValue::new(CommunicationType::GetIotaData)
|
||||||
.with_id(msg_id)
|
.with_id(msg_id)
|
||||||
.add_typed_default(DataType::PublicKey, DataValue::Str(public_key))
|
.add_typed_default(DataType::PublicKey, DataValue::Str(public_key.to_base64()))
|
||||||
.add_typed_default(DataType::IotaId, DataValue::SignedNumber(iota_id.into()));
|
.add_typed_default(DataType::IotaId, DataValue::SignedNumber(iota_id.into()));
|
||||||
|
|
||||||
if let Some(uid) = user_id {
|
if let Some(uid) = user_id {
|
||||||
|
|
@ -752,10 +587,15 @@ impl OmikronConnection {
|
||||||
) -> OmikronResult<()> {
|
) -> OmikronResult<()> {
|
||||||
let iota_id_opt = cv.get_data(DataType::IotaId).as_number().map(|n| n as i64);
|
let iota_id_opt = cv.get_data(DataType::IotaId).as_number().map(|n| n as i64);
|
||||||
|
|
||||||
if let Some(public_key) = cv.get_data(DataType::PublicKey).as_str() {
|
let public_key = cv
|
||||||
|
.get_data(DataType::PublicKey)
|
||||||
|
.as_str()
|
||||||
|
.and_then(|s| PublicKeyBundle::from_base64(s).ok());
|
||||||
|
|
||||||
|
if let Some(public_key) = public_key {
|
||||||
if let Some(iota_id) = iota_id_opt {
|
if let Some(iota_id) = iota_id_opt {
|
||||||
// Register existing IOTA
|
// Register existing IOTA
|
||||||
match sql::register_complete_iota(iota_id, public_key.to_string()).await {
|
match sql::register_complete_iota(iota_id, public_key).await {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
let response = CommunicationValue::new(CommunicationType::Success)
|
let response = CommunicationValue::new(CommunicationType::Success)
|
||||||
.with_id(cv.get_id());
|
.with_id(cv.get_id());
|
||||||
|
|
@ -770,7 +610,7 @@ impl OmikronConnection {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Create new IOTA
|
// Create new IOTA
|
||||||
match sql::create_new_iota(public_key.to_string()).await {
|
match sql::create_new_iota(public_key).await {
|
||||||
Ok(new_iota_id) => {
|
Ok(new_iota_id) => {
|
||||||
let response =
|
let response =
|
||||||
CommunicationValue::new(CommunicationType::CompleteRegisterIota)
|
CommunicationValue::new(CommunicationType::CompleteRegisterIota)
|
||||||
|
|
@ -807,7 +647,7 @@ impl OmikronConnection {
|
||||||
let public_key = cv
|
let public_key = cv
|
||||||
.get_data(DataType::PublicKey)
|
.get_data(DataType::PublicKey)
|
||||||
.as_str()
|
.as_str()
|
||||||
.map(|s| s.to_string());
|
.and_then(|s| PublicKeyBundle::from_base64(s).ok());
|
||||||
let iota_id = cv.get_sender();
|
let iota_id = cv.get_sender();
|
||||||
let reset_token = cv
|
let reset_token = cv
|
||||||
.get_data(DataType::ResetToken)
|
.get_data(DataType::ResetToken)
|
||||||
|
|
@ -873,15 +713,13 @@ impl OmikronConnection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let (Some(public_key), Some(private_key_hash)) = (
|
if let (Some(public_key), Some(private_key_hash)) = (
|
||||||
cv.get_data(DataType::PublicKey).as_str(),
|
cv.get_data(DataType::PublicKey)
|
||||||
|
.as_str()
|
||||||
|
.and_then(|s| PublicKeyBundle::from_base64(s).ok()),
|
||||||
cv.get_data(DataType::PrivateKeyHash).as_str(),
|
cv.get_data(DataType::PrivateKeyHash).as_str(),
|
||||||
) {
|
) {
|
||||||
if let Err(e) = sql::change_keys(
|
if let Err(e) =
|
||||||
user_id,
|
sql::change_keys(user_id, public_key, private_key_hash.to_string()).await
|
||||||
public_key.to_string(),
|
|
||||||
private_key_hash.to_string(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
{
|
{
|
||||||
success = false;
|
success = false;
|
||||||
error_message = e.to_string();
|
error_message = e.to_string();
|
||||||
|
|
@ -1051,9 +889,6 @@ impl OmikronConnection {
|
||||||
.with_id(cv.get_id());
|
.with_id(cv.get_id());
|
||||||
let _ = self.send(&response).await;
|
let _ = self.send(&response).await;
|
||||||
|
|
||||||
// Sync with Tauri
|
|
||||||
crate::notifications::tauri::remove_notification(receiver_id, other_id).await;
|
|
||||||
|
|
||||||
// Sync with other Omikron clients
|
// Sync with other Omikron clients
|
||||||
let sync_cv = CommunicationValue::new(CommunicationType::ReadNotification)
|
let sync_cv = CommunicationValue::new(CommunicationType::ReadNotification)
|
||||||
.with_receiver(receiver_id as u64)
|
.with_receiver(receiver_id as u64)
|
||||||
|
|
@ -1091,9 +926,6 @@ impl OmikronConnection {
|
||||||
CommunicationValue::new(CommunicationType::PushNotification).with_id(cv.get_id());
|
CommunicationValue::new(CommunicationType::PushNotification).with_id(cv.get_id());
|
||||||
let _ = self.send(&response).await;
|
let _ = self.send(&response).await;
|
||||||
|
|
||||||
// Sync with Tauri
|
|
||||||
crate::notifications::tauri::send_notification(receiver_id, sender_id).await;
|
|
||||||
|
|
||||||
// Sync with other Omikron clients
|
// Sync with other Omikron clients
|
||||||
let push_cv = CommunicationValue::new(CommunicationType::PushNotification)
|
let push_cv = CommunicationValue::new(CommunicationType::PushNotification)
|
||||||
.with_receiver(receiver_id as u64)
|
.with_receiver(receiver_id as u64)
|
||||||
|
|
@ -1189,12 +1021,10 @@ impl OmikronConnection {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn cleanup(self: Arc<Self>) {
|
async fn cleanup(self: Arc<Self>) {
|
||||||
if let Some(omikron_id) = self.state.read().await.omikron_id() {
|
if self.id != 0 {
|
||||||
if omikron_id != 0 {
|
log_in!(self.id as i64, PrintType::Omega, "Omikron disconnected");
|
||||||
log_in!(omikron_id, PrintType::Omega, "Omikron disconnected");
|
omikron_manager::remove_omikron(self.id as i64).await;
|
||||||
omikron_manager::remove_omikron(omikron_id).await;
|
user_online_tracker::untrack_omikron(self.id as i64).await;
|
||||||
user_online_tracker::untrack_omikron(omikron_id).await;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(handle) = self.cleanup_handle.lock().unwrap().take() {
|
if let Some(handle) = self.cleanup_handle.lock().unwrap().take() {
|
||||||
|
|
@ -1202,13 +1032,8 @@ impl OmikronConnection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Public API for external use
|
|
||||||
pub async fn is_authenticated(self: Arc<Self>) -> bool {
|
|
||||||
self.state.read().await.is_authenticated()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_omikron_id(self: Arc<Self>) -> Option<i64> {
|
pub async fn get_omikron_id(self: Arc<Self>) -> Option<i64> {
|
||||||
self.state.read().await.omikron_id()
|
Some(self.id as i64)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send_message(self: Arc<Self>, cv: &CommunicationValue) -> OmikronResult<()> {
|
pub async fn send_message(self: Arc<Self>, cv: &CommunicationValue) -> OmikronResult<()> {
|
||||||
|
|
@ -1220,27 +1045,60 @@ impl OmikronConnection {
|
||||||
// Server Startup
|
// Server Startup
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
|
pub async fn get_by_omikron_id(
|
||||||
|
omikron_id: u64,
|
||||||
|
_description: Option<String>,
|
||||||
|
) -> Option<PublicKeyBundle> {
|
||||||
|
sql::get_omikron_by_id(omikron_id as i64)
|
||||||
|
.await
|
||||||
|
.ok()
|
||||||
|
.map(|(bundle, _ip_address)| bundle)
|
||||||
|
}
|
||||||
|
pub async fn complete_register(pub_key: PublicKeyBundle, description: Option<String>) -> u64 {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn start(port: u16) -> Result<(), Box<dyn std::error::Error>> {
|
pub async fn start(port: u16) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let cert_pem = load_file_vec("certs", "transport_cert.pem").expect("Error loading Pemfile");
|
let cert_pem = load_file_vec("certs", "transport_cert.pem").expect("Error loading Pemfile");
|
||||||
|
|
||||||
let key_pem = load_file_vec("certs", "transport_key.pem").expect("Error loading Keyfile");
|
let key_pem = load_file_vec("certs", "transport_key.pem").expect("Error loading Keyfile");
|
||||||
|
|
||||||
let mut host: MTPHost = MTPHost::new(
|
let host_config = HostConfig::new(
|
||||||
HostConfig::new(
|
|
||||||
IpAddr::from(Ipv4Addr::new(0, 0, 0, 0)),
|
IpAddr::from(Ipv4Addr::new(0, 0, 0, 0)),
|
||||||
port,
|
port,
|
||||||
cert_pem,
|
cert_pem,
|
||||||
key_pem,
|
key_pem,
|
||||||
)
|
)
|
||||||
|
.with_policy(Policy {
|
||||||
|
send_mode: SendMode::SingleStreamPerMessage,
|
||||||
|
max_message_size: 1_000_000_000,
|
||||||
|
close_frame_len: u32::MAX,
|
||||||
|
application_close_code: 0,
|
||||||
|
open_stream_timeout: Duration::from_millis(2_000),
|
||||||
|
write_timeout: Duration::from_millis(2_000),
|
||||||
|
accept_stream_timeout: Duration::from_millis(10_000),
|
||||||
|
read_timeout: Duration::from_millis(30_000),
|
||||||
|
keep_alive_interval: Some(Duration::from_secs(6)),
|
||||||
|
max_idle_timeout: Some(Duration::from_secs(30)),
|
||||||
|
force_close_delay: Duration::from_millis(300),
|
||||||
|
max_transient_recv_errors: 20,
|
||||||
|
transient_recv_backoff: Duration::from_millis(100),
|
||||||
|
receiver_queue_capacity: 1000,
|
||||||
|
})
|
||||||
|
.with_authentication(
|
||||||
|
load_keyring(),
|
||||||
|
Box::new(|user_id, description| Box::pin(get_by_omikron_id(user_id, description))),
|
||||||
|
Box::new(|pub_key, description| Box::pin(complete_register(pub_key, description))),
|
||||||
)
|
)
|
||||||
.await?;
|
.with_authentication_policy(AuthenticationPolicy::ForceAuthentication);
|
||||||
|
|
||||||
|
let mut host: Host = Host::new(host_config).await?;
|
||||||
log!("OmikronServer listening on port {}", port);
|
log!("OmikronServer listening on port {}", port);
|
||||||
|
|
||||||
while let Some((sender, mut receiver)) = host.next().await {
|
while let Ok(Some(mut connection)) = host.accept().await {
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let conn = OmikronConnection::new(sender);
|
let conn = OmikronConnection::new(connection.sender);
|
||||||
conn.handle(&mut receiver).await;
|
conn.handle(&mut connection.receiver).await;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue