diff --git a/Cargo.lock b/Cargo.lock index ce8f424..1fd3a15 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -104,7 +104,7 @@ checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] @@ -238,9 +238,9 @@ checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" [[package]] name = "cc" -version = "1.4.3" +version = "1.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "509591b7bcd67f4ef775afad7662703b4935daaa6ec0e5605cfb1090b32a2b6d" +checksum = "0ad534f4357a5264cce5019c989cf66a4f0dc4e0d1b1d15f8aacec0ff7360273" dependencies = [ "find-msvc-tools", "jobserver", @@ -338,9 +338,9 @@ checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a" [[package]] name = "combine" -version = "4.6.7" +version = "4.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +checksum = "cfc320937d09e6de266b31b9afb480f197d7a861be86be7cb2ea7e5d1bfffc5e" dependencies = [ "bytes", "memchr", @@ -572,7 +572,7 @@ checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] @@ -614,9 +614,9 @@ dependencies = [ [[package]] name = "either" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d" +checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34" [[package]] name = "enum-as-inner" @@ -771,7 +771,7 @@ checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] @@ -854,9 +854,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.16" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9f37a958b41b3b19ee2707c06439c0e9e547e847223eb791ecb0cb821c65e27" +checksum = "ef8e5e5a340588f4452631496976cf8636d4a7ecf600239fdc27615d2530bc16" dependencies = [ "atomic-waker", "bytes", @@ -1170,9 +1170,9 @@ checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa" [[package]] name = "icu_provider" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92a7ed671a6aad807a8651a2e1782a6598fda9ce5185dd8158549e95a91c6428" +checksum = "d27bbb9d3abbefac45d55f647c9de1d44aafcd1186eb91879afef17c396c3e73" dependencies = [ "displaydoc", "icu_locale_core", @@ -1397,9 +1397,9 @@ dependencies = [ [[package]] name = "keccak" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffd9697dc4a9a62e2da93389f34400b77a28f0287711263cabb203b3ccb9c0e4" +checksum = "d8f198d1db720e4940b5a493201d199d9f24f568f8f746bd13706243a2f71598" dependencies = [ "cfg-if", "cpufeatures 0.3.0", @@ -1517,9 +1517,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.33" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" [[package]] name = "lru-cache" @@ -1615,7 +1615,7 @@ dependencies = [ [[package]] name = "mtp" version = "0.3.0" -source = "git+https://git.methanium.net/Methanium/mtp.git#a5c8d4f0c898c78351e9d54124886c86e789a22a" +source = "git+https://git.methanium.net/Methanium/mtp.git#c30315af944bda05ecdb9c4e9cc350cb99cf7b2e" dependencies = [ "mtp-client", "mtp-codec", @@ -1631,7 +1631,7 @@ dependencies = [ [[package]] name = "mtp-client" version = "0.3.0" -source = "git+https://git.methanium.net/Methanium/mtp.git#a5c8d4f0c898c78351e9d54124886c86e789a22a" +source = "git+https://git.methanium.net/Methanium/mtp.git#c30315af944bda05ecdb9c4e9cc350cb99cf7b2e" dependencies = [ "mtp-codec", "mtp-common", @@ -1644,7 +1644,7 @@ dependencies = [ [[package]] name = "mtp-codec" version = "0.3.0" -source = "git+https://git.methanium.net/Methanium/mtp.git#a5c8d4f0c898c78351e9d54124886c86e789a22a" +source = "git+https://git.methanium.net/Methanium/mtp.git#c30315af944bda05ecdb9c4e9cc350cb99cf7b2e" dependencies = [ "base64 0.23.1", "byteorder", @@ -1658,10 +1658,9 @@ dependencies = [ [[package]] name = "mtp-common" version = "0.3.0" -source = "git+https://git.methanium.net/Methanium/mtp.git#a5c8d4f0c898c78351e9d54124886c86e789a22a" +source = "git+https://git.methanium.net/Methanium/mtp.git#c30315af944bda05ecdb9c4e9cc350cb99cf7b2e" dependencies = [ "quinn", - "rustls", "thiserror 2.0.20", "wtransport", ] @@ -1669,7 +1668,7 @@ dependencies = [ [[package]] name = "mtp-crypto" version = "0.3.0" -source = "git+https://git.methanium.net/Methanium/mtp.git#a5c8d4f0c898c78351e9d54124886c86e789a22a" +source = "git+https://git.methanium.net/Methanium/mtp.git#c30315af944bda05ecdb9c4e9cc350cb99cf7b2e" dependencies = [ "argon2", "base64 0.22.1", @@ -1692,7 +1691,7 @@ dependencies = [ [[package]] name = "mtp-files" version = "0.3.0" -source = "git+https://git.methanium.net/Methanium/mtp.git#a5c8d4f0c898c78351e9d54124886c86e789a22a" +source = "git+https://git.methanium.net/Methanium/mtp.git#c30315af944bda05ecdb9c4e9cc350cb99cf7b2e" dependencies = [ "mtp-crypto", "rand 0.10.2", @@ -1703,7 +1702,7 @@ dependencies = [ [[package]] name = "mtp-host" version = "0.3.0" -source = "git+https://git.methanium.net/Methanium/mtp.git#a5c8d4f0c898c78351e9d54124886c86e789a22a" +source = "git+https://git.methanium.net/Methanium/mtp.git#c30315af944bda05ecdb9c4e9cc350cb99cf7b2e" dependencies = [ "mtp-codec", "mtp-common", @@ -1719,7 +1718,7 @@ dependencies = [ [[package]] name = "mtp-transport" version = "0.3.0" -source = "git+https://git.methanium.net/Methanium/mtp.git#a5c8d4f0c898c78351e9d54124886c86e789a22a" +source = "git+https://git.methanium.net/Methanium/mtp.git#c30315af944bda05ecdb9c4e9cc350cb99cf7b2e" dependencies = [ "async-trait", "mtp-codec", @@ -1739,7 +1738,7 @@ dependencies = [ [[package]] name = "mtp-type-map" version = "0.3.0" -source = "git+https://git.methanium.net/Methanium/mtp.git#a5c8d4f0c898c78351e9d54124886c86e789a22a" +source = "git+https://git.methanium.net/Methanium/mtp.git#c30315af944bda05ecdb9c4e9cc350cb99cf7b2e" dependencies = [ "serde", "serde_yaml", @@ -1748,7 +1747,7 @@ dependencies = [ [[package]] name = "mtp-webserver" version = "0.3.0" -source = "git+https://git.methanium.net/Methanium/mtp.git#a5c8d4f0c898c78351e9d54124886c86e789a22a" +source = "git+https://git.methanium.net/Methanium/mtp.git#c30315af944bda05ecdb9c4e9cc350cb99cf7b2e" dependencies = [ "async-trait", "bytes", @@ -2392,9 +2391,9 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "rand" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a" +checksum = "e058c7de0b26af77780c769414d6257830bb240f3c38477dbc2c16e5f54d6d4c" dependencies = [ "libc", "rand_chacha 0.3.1", @@ -2690,9 +2689,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.103.14" +version = "0.103.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0527518605e68109d875e248ea259b6758801cf165e4b2c2733ae3b51f12535a" +checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2" dependencies = [ "aws-lc-rs", "ring", @@ -2792,7 +2791,7 @@ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] @@ -2883,7 +2882,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09057cb2149ad4cbd2da1e26b351f9a4c354219421229c69c3063e6f61947c4a" dependencies = [ "digest 0.11.3", - "keccak 0.2.1", + "keccak 0.2.2", "sponge-cursor", ] @@ -3019,9 +3018,9 @@ dependencies = [ [[package]] name = "syn" -version = "3.0.3" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" +checksum = "e6275cddf4610d1775e6d1fe9469b2e77d0f39fd98fb7450901b821e0c53649f" dependencies = [ "proc-macro2", "quote", @@ -3098,7 +3097,7 @@ checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] @@ -3181,7 +3180,7 @@ checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] @@ -3328,7 +3327,7 @@ dependencies = [ "idna 0.4.0", "ipnet", "once_cell", - "rand 0.8.7", + "rand 0.8.8", "smallvec", "thiserror 1.0.69", "tinyvec", @@ -3349,7 +3348,7 @@ dependencies = [ "lru-cache", "once_cell", "parking_lot", - "rand 0.8.7", + "rand 0.8.8", "resolv-conf", "smallvec", "thiserror 1.0.69", @@ -3458,9 +3457,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.24.1" +version = "1.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cefc03fd367c0c6d4305de1b312cf00248c4114f4a0418ce6a6af769e3b0bd9" +checksum = "b5772d71c9be8a8a6ac2117d949c5b224c1b72241bb611d9a3012edcf8af7812" dependencies = [ "getrandom 0.4.3", "js-sys", @@ -4006,9 +4005,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.7" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b5c6b5976d66c1d703c4fd17d3f5e43c8cedaacf604961b171adc7130896d8" +checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8" dependencies = [ "yoke", "zerofrom", @@ -4017,13 +4016,13 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f212a141d820099d57ffafb9569be9617a6f27d3dc881fbee8fb56642f917a9" +checksum = "34df6fc39dbd26ddc9c10e6a2984476e13acce22e64e4487636ef494369225da" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] diff --git a/flake.nix b/flake.nix index 282d1f3..966e55d 100644 --- a/flake.nix +++ b/flake.nix @@ -145,6 +145,12 @@ description = "Port the Omikron server listens on for incoming QUIC connections."; }; + bindAddress = lib.mkOption { + type = lib.types.str; + default = "0.0.0.0"; + description = "IP address to bind the Omikron server to."; + }; + omegaHost = lib.mkOption { type = lib.types.str; default = "tensamin.net"; @@ -189,6 +195,7 @@ environment = { RHO_PORT = toString cfg.rhoPort; + BIND_ADDRESS = cfg.bindAddress; OMEGA_HOST = cfg.omegaHost; OMEGA_PORT = toString cfg.omegaPort; }; diff --git a/mtp-type-maps b/mtp-type-maps index a297dcc..486541b 160000 --- a/mtp-type-maps +++ b/mtp-type-maps @@ -1 +1 @@ -Subproject commit a297dcce60bc6e84696c6a16f5fd510beb2ca643 +Subproject commit 486541b9483356ff49ff3ec7016f87d3ecbeaa0e diff --git a/src/anonymous_clients/anonymous_client_connection.rs b/src/anonymous_clients/anonymous_client_connection.rs index 04b9da1..884936e 100644 --- a/src/anonymous_clients/anonymous_client_connection.rs +++ b/src/anonymous_clients/anonymous_client_connection.rs @@ -31,7 +31,7 @@ pub struct AnonymousClientConnection { impl AnonymousClientConnection { pub async fn from_general(general: Arc, user_id: u64) -> Arc { - let username: String = generate_username(); + let username = generate_username(user_id); Arc::new(Self { state: general.state.clone(), user_id: user_id, @@ -47,6 +47,7 @@ impl AnonymousClientConnection { }) } pub fn start(self: Arc) { + anonymous_manager::add_anonymous_user(self.clone()); let self_clone = self.clone(); tokio::spawn(async move { while let Ok(cv) = self_clone.receiver.receive().await { @@ -632,10 +633,13 @@ impl AnonymousClientConnection { } } - /// Handle connection close pub async fn handle_close(&self) { - - // TODO delete temp user + *self.is_open.write().await = false; + self.state + .call_manager + .remove_user_from_calls(self.user_id) + .await; + anonymous_manager::remove_anonymous_user(self.user_id).await; } } diff --git a/src/anonymous_clients/anonymous_manager.rs b/src/anonymous_clients/anonymous_manager.rs index 41703bf..e03373c 100644 --- a/src/anonymous_clients/anonymous_manager.rs +++ b/src/anonymous_clients/anonymous_manager.rs @@ -1,4 +1,5 @@ use dashmap::DashMap; +use dashmap::mapref::entry::Entry; use once_cell::sync::Lazy; use rand::prelude::{IndexedRandom, RngExt}; use std::sync::Arc; @@ -7,15 +8,20 @@ use crate::anonymous_clients::anonymous_client_connection::AnonymousClientConnec static ANONYMOUS_USERS: Lazy>> = Lazy::new(|| DashMap::new()); +static ANONYMOUS_USERNAMES: Lazy> = Lazy::new(|| DashMap::new()); -#[allow(dead_code)] -pub async fn add_anonymous_user(connection: Arc) { +pub fn add_anonymous_user(connection: Arc) { ANONYMOUS_USERS.insert(connection.get_user_id(), connection); } #[allow(dead_code)] pub async fn remove_anonymous_user(user_id: u64) { - ANONYMOUS_USERS.remove(&user_id); + if let Some((_, connection)) = ANONYMOUS_USERS.remove(&user_id) { + let username = connection.get_user_name().await; + ANONYMOUS_USERNAMES.remove_if(&username, |_, reserved_user_id| { + *reserved_user_id == user_id + }); + } } pub async fn get_anonymous_user(user_id: u64) -> Option> { @@ -25,30 +31,52 @@ pub async fn get_anonymous_user(user_id: u64) -> Option Option> { - let users: Vec<_> = ANONYMOUS_USERS - .iter() - .map(|ref_multi| ref_multi.value().clone()) - .collect(); - - for user_conn in users { - if user_conn.get_user_name().await == username { - return Some(user_conn); - } - } - - return None; + let user_id = ANONYMOUS_USERNAMES + .get(&username.to_lowercase())? + .value() + .to_owned(); + get_anonymous_user(user_id).await } -// TODO: implement check if taken -pub fn generate_username() -> String { +pub fn generate_username(user_id: u64) -> String { let adjectives = ["Swift", "Clever", "Brave", "Sneaky", "Fierce"]; let nouns = ["Tiger", "Eagle", "Shark", "Wolf", "Dragon"]; let mut rng = rand::rng(); - let adj = adjectives.choose(&mut rng).unwrap(); - let noun = nouns.choose(&mut rng).unwrap(); + loop { + let Some(adj) = adjectives.choose(&mut rng) else { + continue; + }; + let Some(noun) = nouns.choose(&mut rng) else { + continue; + }; + let username = format!("{}{}{}", adj, noun, rng.random_range(0..10000)); + let canonical_username = username.to_lowercase(); - let number: u16 = rng.random_range(0..10000); + if reserve_username(canonical_username, user_id) { + return username; + } + } +} - format!("{}{}{}", adj, noun, number) +fn reserve_username(username: String, user_id: u64) -> bool { + if let Entry::Vacant(entry) = ANONYMOUS_USERNAMES.entry(username) { + entry.insert(user_id); + true + } else { + false + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn username_reservation_is_unique() { + let username = "anonymous-manager-reservation-test".to_string(); + assert!(reserve_username(username.clone(), 1)); + assert!(!reserve_username(username.clone(), 2)); + ANONYMOUS_USERNAMES.remove(&username); + } } diff --git a/src/app_state.rs b/src/app_state.rs index 525f469..bb986fe 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -113,38 +113,3 @@ impl AppState { Keyring::from_bytes(&bytes).map_err(|error| error.to_string()) } } - -#[cfg(test)] -mod tests { - use super::*; - use std::net::{IpAddr, Ipv4Addr}; - - #[tokio::test] - async fn permits_are_released_after_a_session_ends() { - let limits = RhoConnectionLimits::new(1, 1, 1); - let permit = limits.all.clone().try_acquire_owned().unwrap(); - assert!(limits.all.clone().try_acquire_owned().is_err()); - drop(permit); - assert!(limits.all.clone().try_acquire_owned().is_ok()); - } - - #[tokio::test] - async fn anonymous_pool_does_not_consume_global_authenticated_capacity() { - let limits = RhoConnectionLimits::new(2, 1, 1); - let _anonymous = limits.anonymous.clone().try_acquire_owned().unwrap(); - assert!(limits.anonymous.clone().try_acquire_owned().is_err()); - let _authenticated = limits.all.clone().try_acquire_owned().unwrap(); - assert!(limits.all.clone().try_acquire_owned().is_ok()); - } - - #[test] - fn per_ip_permits_are_released() { - let limits = RhoConnectionLimits::new(2, 2, 1); - let ip = IpAddr::V4(Ipv4Addr::LOCALHOST); - let permit = limits.try_acquire_anonymous_per_ip(ip).unwrap(); - assert!(limits.try_acquire_anonymous_per_ip(ip).is_none()); - drop(permit); - assert_eq!(limits.anonymous_count_for_ip(ip), 0); - assert!(limits.try_acquire_anonymous_per_ip(ip).is_some()); - } -} diff --git a/src/calls/call_group.rs b/src/calls/call_group.rs index 54da303..4626f7e 100755 --- a/src/calls/call_group.rs +++ b/src/calls/call_group.rs @@ -262,6 +262,7 @@ impl CallGroup { .write() .await .retain(|caller| caller.user_id != user_id); + self.secrets.write().await.remove(&user_id); } pub async fn get_short_link(self: Arc) -> Option { diff --git a/src/calls/call_manager.rs b/src/calls/call_manager.rs index 93e5789..47191e7 100644 --- a/src/calls/call_manager.rs +++ b/src/calls/call_manager.rs @@ -78,6 +78,13 @@ impl CallManager { call_groups } + pub async fn remove_user_from_calls(&self, user_id: u64) { + let call_groups = self.get_call_groups(user_id).await; + for call_group in call_groups { + call_group.remove_caller(user_id).await; + } + } + pub async fn get_call_token(&self, user_id: u64, call_id: Uuid) -> Result { if let Some(cg) = self.groups.get(&call_id) { let mut members = cg.members.write().await; @@ -221,6 +228,30 @@ mod tests { ); } + #[tokio::test] + async fn removing_user_from_calls_removes_membership_and_invitation_secret() { + let call_id = Uuid::new_v4(); + let sender_id = 55; + let anonymous_user_id = 66; + let group = Arc::new(CallGroup::new( + call_id, + Arc::new(Caller::new(sender_id, call_id, true)), + )); + let manager = CallManager::default(); + manager.groups.insert(call_id, group.clone()); + + assert!( + manager + .add_invite(call_id, sender_id, anonymous_user_id, envelope("anonymous"),) + .await + ); + + manager.remove_user_from_calls(anonymous_user_id).await; + + assert!(group.get_caller(anonymous_user_id).await.is_none()); + assert!(group.get_secret_for_user(anonymous_user_id).await.is_none()); + } + #[test] fn self_invites_are_not_forwarded() { let manager = CallManager::default(); diff --git a/src/config.rs b/src/config.rs index 8137cde..e69f383 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,8 +1,9 @@ -use std::{env, time::Duration}; +use std::{env, net::IpAddr, time::Duration}; use thiserror::Error; const DEFAULT_RHO_PORT: u16 = 443; +const DEFAULT_BIND_ADDRESS: &str = "0.0.0.0"; const DEFAULT_OMEGA_HOST: &str = "tensamin.net"; const DEFAULT_OMEGA_PORT: u16 = 9187; const DEFAULT_OMEGA_SYNC_TIMEOUT_SECONDS: u64 = 20; @@ -21,6 +22,7 @@ pub struct LiveKitConfig { #[derive(Clone, Debug, Eq, PartialEq)] pub struct Config { pub rho_port: u16, + pub bind_address: IpAddr, pub omega_host: String, pub omega_port: u16, pub omikron_id: u64, @@ -52,6 +54,7 @@ pub enum ConfigError { impl Config { pub fn from_environment() -> Result { let rho_port = parse_or_default("RHO_PORT", DEFAULT_RHO_PORT)?; + let bind_address = parse_bind_address(env::var("BIND_ADDRESS").ok())?; let omega_port = parse_or_default("OMEGA_PORT", DEFAULT_OMEGA_PORT)?; let omikron_id = parse_or_default("ID", 0_u64)?; // Each synchronization request is bounded by this timeout. After @@ -87,6 +90,7 @@ impl Config { Ok(Self { rho_port, + bind_address, omega_host, omega_port, omikron_id, @@ -100,15 +104,16 @@ impl Config { } } -fn parse_positive_or_default(name: &'static str, default: usize) -> Result { - let value = parse_or_default(name, default)?; - if value == 0 { - return Err(ConfigError::InvalidValue { - name, - kind: "positive number", - }); - } - Ok(value) +fn parse_bind_address(value: Option) -> Result { + value + .as_deref() + .unwrap_or(DEFAULT_BIND_ADDRESS) + .trim() + .parse() + .map_err(|_| ConfigError::InvalidValue { + name: "BIND_ADDRESS", + kind: "IP address", + }) } fn parse_or_default(name: &'static str, default: T) -> Result @@ -181,4 +186,23 @@ mod tests { })) ); } + + #[test] + fn parses_configured_bind_address() { + assert_eq!( + parse_bind_address(Some("10.200.2.0".to_string())), + Ok(IpAddr::V4(std::net::Ipv4Addr::new(10, 200, 2, 0))) + ); + } + + #[test] + fn rejects_invalid_bind_address() { + assert_eq!( + parse_bind_address(Some("not-an-address".to_string())), + Err(ConfigError::InvalidValue { + name: "BIND_ADDRESS", + kind: "IP address", + }) + ); + } } diff --git a/src/main.rs b/src/main.rs index 34f54eb..7202ca2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,7 +9,7 @@ mod rho; mod services; mod util; -use std::path::PathBuf; +use std::{fmt::Write, path::PathBuf}; use dotenv::dotenv; use once_cell::sync::Lazy; @@ -30,7 +30,7 @@ use crate::{ omega::omega_connection::{OmegaConnection, start_task_cleanup_loop}, rho::rho_manager::RhoManager, rho::server::start, - util::logger::startup, + util::logger::{PrintType, startup}, }; #[tokio::main] @@ -57,15 +57,18 @@ async fn main() { return; } }; - let keyring = - match load_or_create_keyring(identity_secret.as_slice(), KEYRING_PATH, PUBLIC_KEY_PATH) { - Ok(keyring) => keyring, - Err(error) => { - eprintln!("Unable to load Omikron identity: {error}"); - return; - } - }; - drop(identity_secret); + let public_key = match omega_database_public_key(&keyring) { + Ok(public_key) => public_key, + Err(error) => { + eprintln!("Unable to serialize public key for Omega: {error}"); + return; + } + }; + log!( + 0, + PrintType::Omikron, + "Omikron public_key for Omega enrollment: {public_key}" + ); let omega_keyring = match keyring_for_omega(&keyring) { Ok(keyring) => keyring, @@ -100,3 +103,34 @@ fn keyring_for_omega(keyring: &Keyring) -> Result { let bytes = keyring.try_to_bytes().map_err(|error| error.to_string())?; Keyring::from_bytes(&bytes).map_err(|error| error.to_string()) } + +fn omega_database_public_key(keyring: &Keyring) -> Result { + let public_key = keyring + .public_key_bundle() + .try_as_bytes() + .map_err(|error| error.to_string())?; + omega_database_blob_literal(&public_key) +} + +fn omega_database_blob_literal(public_key: &[u8]) -> Result { + let mut literal = String::with_capacity(3 + public_key.len() * 2); + literal.push_str("X'"); + for byte in public_key { + write!(&mut literal, "{byte:02X}").map_err(|error| error.to_string())?; + } + literal.push('\''); + Ok(literal) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn public_key_blob_literal_is_valid_mysql_hex_syntax() { + assert_eq!( + omega_database_blob_literal(&[0x00, 0x1A, 0xFF]), + Ok("X'001AFF'".to_string()) + ); + } +} diff --git a/src/rho/server.rs b/src/rho/server.rs index 445e27c..b94885b 100644 --- a/src/rho/server.rs +++ b/src/rho/server.rs @@ -1,4 +1,3 @@ -use std::net::{IpAddr, Ipv4Addr}; use std::sync::Arc; use std::time::Duration; @@ -143,7 +142,7 @@ pub async fn start(state: Arc) -> Result<(), Box) -> Result<(), Box, version: Version) - let key_str = key.to_string(); match value { - DataValue::Str(s) => format!("{}=\"{}\"", key_str, s), + DataValue::Str(s) => format!("{}=\"{}\"", key_str, abbreviate_string(&s)), DataValue::Container(inner) => { let inner_formatted = format_data_container(inner, version.clone()); @@ -238,7 +238,7 @@ fn format_array(arr: Vec, version: Version) -> String { let parts: Vec = arr .into_iter() .map(|value| match value { - DataValue::Str(s) => format!("\"{}\"", s), + DataValue::Str(s) => format!("\"{}\"", abbreviate_string(&s)), DataValue::Container(inner) => { let inner_formatted = format_data_container(inner, version.clone()); @@ -266,6 +266,31 @@ fn format_array(arr: Vec, version: Version) -> String { parts.join(", ") } +fn abbreviate_string(value: &str) -> String { + const EDGE_LENGTH: usize = 4; + + let chars: Vec = value.chars().collect(); + if chars.len() <= EDGE_LENGTH * 2 { + return value.to_string(); + } + + let prefix: String = chars.iter().take(EDGE_LENGTH).collect(); + let suffix: String = chars.iter().rev().take(EDGE_LENGTH).rev().collect(); + format!("{prefix}...{suffix}") +} + +#[cfg(test)] +mod tests { + use super::abbreviate_string; + + #[test] + fn abbreviates_only_strings_longer_than_eight_characters() { + assert_eq!(abbreviate_string("12345678"), "12345678"); + assert_eq!(abbreviate_string("123456789"), "1234...6789"); + assert_eq!(abbreviate_string("YWJjZGVmZ2hpag=="), "YWJj...ag=="); + } +} + #[macro_export] macro_rules! log_cv { ($kind:expr, $cv:expr) => {