diff --git a/Cargo.lock b/Cargo.lock index f2ea815..ee77e55 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -310,6 +310,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + [[package]] name = "const-oid" version = "0.10.2" @@ -473,14 +479,25 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06" +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid 0.9.6", + "pem-rfc7468 0.7.0", + "zeroize", +] + [[package]] name = "der" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a69dedd701da44b0536442edf09c81a64b0ab97a7a4a5e3d1971f00027cbc63d" dependencies = [ - "const-oid", - "pem-rfc7468", + "const-oid 0.10.2", + "pem-rfc7468 1.0.0", "zeroize", ] @@ -511,6 +528,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", + "const-oid 0.9.6", "crypto-common 0.1.7", "subtle", ] @@ -522,7 +540,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" dependencies = [ "block-buffer 0.12.1", - "const-oid", + "const-oid 0.10.2", "crypto-common 0.2.2", "ctutils", ] @@ -562,8 +580,8 @@ version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29fcf32e6c73d1079f83ab4d782de2d81620346a5f38c6237a86a22f8368980a" dependencies = [ - "pkcs8", - "signature", + "pkcs8 0.11.0", + "signature 3.0.0", ] [[package]] @@ -576,7 +594,7 @@ dependencies = [ "ed25519", "serde", "sha2 0.11.0", - "signature", + "signature 3.0.0", "subtle", "zeroize", ] @@ -608,12 +626,13 @@ dependencies = [ [[package]] name = "etcetera" -version = "0.11.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de48cc4d1c1d97a20fd819def54b890cadde72ed3ad0c614822a0a433361be96" +checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" dependencies = [ "cfg-if", - "windows-sys 0.61.2", + "home", + "windows-sys 0.48.0", ] [[package]] @@ -632,7 +651,7 @@ version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef975e30683b2d965054bb0a836f8973857c4ebf6acf274fe46617cd285060d8" dependencies = [ - "foldhash", + "foldhash 0.2.0", "libm", "portable-atomic", "siphasher", @@ -664,9 +683,9 @@ checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890" [[package]] name = "flume" -version = "0.12.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e139bc46ca777eb5efaf62df0ab8cc5fd400866427e56c68b22e414e53bd3be" +checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" dependencies = [ "futures-core", "futures-sink", @@ -679,6 +698,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "foldhash" version = "0.2.0" @@ -919,13 +944,13 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hashbrown" -version = "0.16.1" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "equivalent", - "foldhash", + "foldhash 0.1.5", ] [[package]] @@ -936,11 +961,11 @@ checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" [[package]] name = "hashlink" -version = "0.11.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824e001ac4f3012dd16a264bec811403a67ca9deb6c102fc5049b32c4574b35f" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" dependencies = [ - "hashbrown 0.16.1", + "hashbrown 0.15.5", ] [[package]] @@ -955,13 +980,31 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac 0.12.1", +] + [[package]] name = "hkdf" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4aaa26c720c68b866f2c96ef5c1264b3e6f473fe5d4ce61cd44bbe913e553018" dependencies = [ - "hmac", + "hmac 0.13.0", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", ] [[package]] @@ -973,6 +1016,15 @@ dependencies = [ "digest 0.11.3", ] +[[package]] +name = "home" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "httlib-huffman" version = "0.3.4" @@ -1036,9 +1088,9 @@ dependencies = [ [[package]] name = "hyper" -version = "1.11.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b501faa50e7a26c3d3560ca625132f4078a17771f4810baf70475ae48cbe43" +checksum = "d22053281f852e11534f5198498373cbb59295120a20771d90f7ed1897490a72" dependencies = [ "atomic-waker", "bytes", @@ -1175,9 +1227,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.14.1" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07aa2048142242915a31d35844fb311e0e53fcca590c3a0a40dcf1b841fa09eb" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", "hashbrown 0.17.1", @@ -1292,6 +1344,9 @@ name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] [[package]] name = "libc" @@ -1306,10 +1361,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] -name = "libsqlite3-sys" -version = "0.37.0" +name = "libredox" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f111c8c41e7c61a49cd34e44c7619462967221a6443b0ec299e0ac30cfb9b1" +checksum = "d7955dfc218a8afb29dfeffd540e3a6e96baeb94fe7138228dd7cc6937fbbf96" +dependencies = [ + "bitflags", + "libc", + "plain", + "redox_syscall 0.9.3", +] + +[[package]] +name = "libsqlite3-sys" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" dependencies = [ "pkg-config", "vcpkg", @@ -1344,12 +1411,12 @@ checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" [[package]] name = "md-5" -version = "0.11.0" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69b6441f590336821bb897fb28fc622898ccceb1d6cea3fde5ea86b090c4de98" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" dependencies = [ "cfg-if", - "digest 0.11.3", + "digest 0.10.7", ] [[package]] @@ -1381,14 +1448,14 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "add6b9d92e496f16f4526d68ff29da1483aba4b119baeab8bed3b9e3544a6f3d" dependencies = [ - "const-oid", + "const-oid 0.10.2", "crypto-common 0.2.2", "ctutils", "hybrid-array", "module-lattice", - "pkcs8", + "pkcs8 0.11.0", "shake", - "signature", + "signature 3.0.0", ] [[package]] @@ -1453,7 +1520,7 @@ dependencies = [ "mtp-common", "mtp-crypto", "mtp-transport", - "rand", + "rand 0.10.2", "tokio", ] @@ -1467,7 +1534,7 @@ dependencies = [ "mtp-common", "mtp-crypto", "mtp-type-map", - "rand", + "rand 0.10.2", "thiserror 2.0.20", ] @@ -1491,10 +1558,10 @@ dependencies = [ "chacha20poly1305", "ed25519-dalek", "getrandom 0.4.3", - "hkdf", + "hkdf 0.13.0", "ml-dsa", "mlkem-tls", - "rand", + "rand 0.10.2", "rand_core 0.6.4", "rustls", "serde", @@ -1510,7 +1577,7 @@ version = "0.3.0" source = "git+https://git.methanium.net/Methanium/mtp.git#f6a8b464e0daae6c7753208f3d8601d21cd47ab2" dependencies = [ "mtp-crypto", - "rand", + "rand 0.10.2", "thiserror 2.0.20", "zeroize", ] @@ -1524,7 +1591,7 @@ dependencies = [ "mtp-common", "mtp-crypto", "mtp-transport", - "rand", + "rand 0.10.2", "thiserror 2.0.20", "tokio", "tracing", @@ -1540,7 +1607,7 @@ dependencies = [ "mtp-codec", "mtp-common", "mtp-crypto", - "rand", + "rand 0.10.2", "rcgen", "rustls", "rustls-native-certs", @@ -1608,6 +1675,22 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-bigint-dig" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" +dependencies = [ + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand 0.8.8", + "smallvec", + "zeroize", +] + [[package]] name = "num-conv" version = "0.2.2" @@ -1623,6 +1706,16 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92800bd69a1eac91786bcfe9da64a897eb72911b8dc3095decbd07429e8048b" +dependencies = [ + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -1630,6 +1723,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -1652,14 +1746,14 @@ name = "omega" version = "0.1.0" dependencies = [ "ansi_term", - "base64 0.23.1", + "base64 0.22.1", "bytes", "dashmap", "dotenv", "http", "mtp", "once_cell", - "rand", + "rand 0.10.2", "rustls", "serde", "serde_json", @@ -1668,6 +1762,7 @@ dependencies = [ "tokio", "tokio-util", "uuid", + "zeroize", ] [[package]] @@ -1712,7 +1807,7 @@ checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.5.18", "smallvec", "windows-link", ] @@ -1739,13 +1834,12 @@ dependencies = [ ] [[package]] -name = "pem" -version = "4.0.0" +name = "pem-rfc7468" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d354a98a3d1251555de99e8fdd8afda05573c31b82f59063a7b0a29b5527f120" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" dependencies = [ - "base64 0.23.1", - "serde_core", + "base64ct", ] [[package]] @@ -1769,14 +1863,35 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der 0.7.10", + "pkcs8 0.10.2", + "spki 0.7.3", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der 0.7.10", + "spki 0.7.3", +] + [[package]] name = "pkcs8" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "451913da69c775a56034ea8d9003d27ee8948e12443eae7c038ba100a4f21cb7" dependencies = [ - "der", - "spki", + "der 0.8.1", + "spki 0.8.0", ] [[package]] @@ -1785,6 +1900,12 @@ version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + [[package]] name = "poly1305" version = "0.8.0" @@ -1817,6 +1938,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + [[package]] name = "proc-macro2" version = "1.0.107" @@ -1858,7 +1988,7 @@ dependencies = [ "fastbloom", "getrandom 0.4.3", "lru-slab", - "rand", + "rand 0.10.2", "rand_pcg", "ring", "rustc-hash", @@ -1901,6 +2031,17 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" +[[package]] +name = "rand" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e058c7de0b26af77780c769414d6257830bb240f3c38477dbc2c16e5f54d6d4c" +dependencies = [ + "libc", + "rand_chacha", + "rand_core 0.6.4", +] + [[package]] name = "rand" version = "0.10.2" @@ -1912,6 +2053,16 @@ dependencies = [ "rand_core 0.10.1", ] +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + [[package]] name = "rand_core" version = "0.6.4" @@ -1938,12 +2089,12 @@ dependencies = [ [[package]] name = "rcgen" -version = "0.14.10" +version = "0.14.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8774e05a7d0de114588e6a28fe7e71694b82614ed569d86d8b389dfbc98b8ad8" +checksum = "091e7a8e7d86e6feb87a27ce8e2cba29d49eff9507afeebefab7eeb2ca667fb4" dependencies = [ "aws-lc-rs", - "pem 4.0.0", + "pem", "ring", "rustls-pki-types", "time", @@ -1960,6 +2111,15 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_syscall" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d678d17679829e73d371e96880897e98fee2ded7acc0a50bdf8af2affa4b2fe5" +dependencies = [ + "bitflags", +] + [[package]] name = "ring" version = "0.17.14" @@ -1974,6 +2134,26 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rsa" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" +dependencies = [ + "const-oid 0.9.6", + "digest 0.10.7", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8 0.10.2", + "rand_core 0.6.4", + "signature 2.2.0", + "spki 0.7.3", + "subtle", + "zeroize", +] + [[package]] name = "rustc-hash" version = "2.1.3" @@ -2183,6 +2363,18 @@ dependencies = [ "zmij", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "serde_yaml" version = "0.9.34+deprecated" @@ -2198,13 +2390,13 @@ dependencies = [ [[package]] name = "sha1" -version = "0.11.0" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214" +checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8" dependencies = [ "cfg-if", - "cpufeatures 0.3.1", - "digest 0.11.3", + "cpufeatures 0.2.17", + "digest 0.10.7", ] [[package]] @@ -2266,6 +2458,16 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + [[package]] name = "signature" version = "3.0.0" @@ -2332,6 +2534,16 @@ dependencies = [ "lock_api", ] +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der 0.7.10", +] + [[package]] name = "spki" version = "0.8.0" @@ -2339,7 +2551,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d9efca8738c78ee9484207732f728b1ef517bbb1833d6fc0879ca898a522f6f" dependencies = [ "base64ct", - "der", + "der 0.8.1", ] [[package]] @@ -2350,9 +2562,9 @@ checksum = "3a0219bd7d979d58245a4f41f695e1ac9f8befdffadd7f61f1bae9e39abc6620" [[package]] name = "sqlx" -version = "0.9.0" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "378620ccc25c62c89d8be1c819e76a88d59bdcc3304733330788948e619bfd71" +checksum = "1fefb893899429669dcdd979aff487bd78f4064e5e7907e4269081e0ef7d97dc" dependencies = [ "sqlx-core", "sqlx-macros", @@ -2363,13 +2575,12 @@ dependencies = [ [[package]] name = "sqlx-core" -version = "0.9.0" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b44e85bf579a8eeb4ceaa77a3a523baf2bf0e9bac7e40f405d537b5d2d5ccb" +checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6" dependencies = [ "base64 0.22.1", "bytes", - "cfg-if", "crc", "crossbeam-queue", "either", @@ -2378,11 +2589,12 @@ dependencies = [ "futures-intrusive", "futures-io", "futures-util", - "hashbrown 0.16.1", + "hashbrown 0.15.5", "hashlink", "indexmap", "log", "memchr", + "once_cell", "percent-encoding", "serde", "serde_json", @@ -2397,9 +2609,9 @@ dependencies = [ [[package]] name = "sqlx-macros" -version = "0.9.0" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd2b84f2bc39a5705ef27ec785a11c934a41bbd4a24941e257927cddc26b60bf" +checksum = "a2d452988ccaacfbf5e0bdbc348fb91d7c8af5bee192173ac3636b5fb6e6715d" dependencies = [ "proc-macro2", "quote", @@ -2410,15 +2622,15 @@ dependencies = [ [[package]] name = "sqlx-macros-core" -version = "0.9.0" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb8d96de5fdc85a5c4ec813432b523ec637e80ba98f046555f75f7908ddac7c3" +checksum = "19a9c1841124ac5a61741f96e1d9e2ec77424bf323962dd894bdb93f37d5219b" dependencies = [ - "cfg-if", "dotenvy", "either", "heck", "hex", + "once_cell", "proc-macro2", "quote", "serde", @@ -2429,42 +2641,57 @@ dependencies = [ "sqlx-postgres", "sqlx-sqlite", "syn 2.0.119", - "thiserror 2.0.20", "tokio", "url", ] [[package]] name = "sqlx-mysql" -version = "0.9.0" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90b8020fe17c5f2c245bfa2505d7ef59c5604839527c740266ad2214acebea27" +checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526" dependencies = [ + "atoi", + "base64 0.22.1", "bitflags", "byteorder", "bytes", "crc", - "digest 0.11.3", + "digest 0.10.7", "dotenvy", "either", + "futures-channel", "futures-core", + "futures-io", "futures-util", "generic-array", + "hex", + "hkdf 0.12.4", + "hmac 0.12.1", + "itoa", "log", + "md-5", + "memchr", + "once_cell", "percent-encoding", + "rand 0.8.8", + "rsa", "serde", "sha1", - "sha2 0.11.0", + "sha2 0.10.9", + "smallvec", "sqlx-core", + "stringprep", "thiserror 2.0.20", "tracing", + "whoami", ] [[package]] name = "sqlx-postgres" -version = "0.9.0" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87a2bdd6e83f6b3ea525ca9fee568030508b58355a43d0b2c1674d5f79dcd65e" +checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46" dependencies = [ "atoi", "base64 0.22.1", @@ -2477,16 +2704,18 @@ dependencies = [ "futures-core", "futures-util", "hex", - "hkdf", - "hmac", + "hkdf 0.12.4", + "hmac 0.12.1", + "home", "itoa", "log", "md-5", "memchr", - "rand", + "once_cell", + "rand 0.8.8", "serde", "serde_json", - "sha2 0.11.0", + "sha2 0.10.9", "smallvec", "sqlx-core", "stringprep", @@ -2497,13 +2726,12 @@ dependencies = [ [[package]] name = "sqlx-sqlite" -version = "0.9.0" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "488e99c397a62007e4229aec669a179816339afc6d2620ca6fa420dbee2e982c" +checksum = "c2d12fe70b2c1b4401038055f90f151b78208de1f9f89a7dbfd41587a10c3eea" dependencies = [ "atoi", "flume", - "form_urlencoded", "futures-channel", "futures-core", "futures-executor", @@ -2513,6 +2741,7 @@ dependencies = [ "log", "percent-encoding", "serde", + "serde_urlencoded", "sqlx-core", "thiserror 2.0.20", "tracing", @@ -2884,6 +3113,12 @@ version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" +[[package]] +name = "wasite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + [[package]] name = "wasm-bindgen" version = "0.2.127" @@ -2950,9 +3185,13 @@ dependencies = [ [[package]] name = "whoami" -version = "2.1.3" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "626c4bac6755d76ffc12cb01b2eac751db1996b9e0041de9aa02c8c211ddc82c" +checksum = "5d4a4db5077702ca3015d3d02d74974948aba2ad9e12ab7df718ee64ccd7e97d" +dependencies = [ + "libredox", + "wasite", +] [[package]] name = "winapi" @@ -2991,13 +3230,22 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -3009,34 +3257,67 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -3049,24 +3330,48 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -3086,7 +3391,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4273ce3157a3262a68665f8d3f20a0ac0c5b8a69ffd67f05ae986832ebec036" dependencies = [ "bytes", - "pem 3.0.6", + "pem", "quinn", "rcgen", "rustls", @@ -3179,6 +3484,26 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zerocopy" +version = "0.8.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556764e583adb45a9f8d413c2a147fa7e8d821e48e12b14fd560b607998b75eb" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ab42fc20575779bd240faa45f94a74256f755c0fa9e89f0ede20d91d0cdfc1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "zerofrom" version = "0.1.8" diff --git a/Cargo.toml b/Cargo.toml index d5f04b3..395ad06 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,28 +7,28 @@ edition = "2024" mtp = { git = "https://git.methanium.net/Methanium/mtp.git", features = [ "crypto", "files", - "raw", "web-server", ] } ansi_term = "0.12.1" -base64 = "0.23.1" +base64 = "0.22.1" bytes = "1" dashmap = "6.2.1" dotenv = "0.15.0" http = "1" once_cell = "1.21.4" rand = "0.10.2" -rustls = { version = "0.23.43", default-features = false, features = [ +rustls = { version = "0.23.42", default-features = false, features = [ "std", "tls12", "aws-lc-rs", "prefer-post-quantum", ] } -sqlx = { version = "0.9.0", features = ["mysql", "runtime-tokio", "migrate"] } +sqlx = { version = "0.8.6", features = ["mysql", "runtime-tokio", "migrate"] } tokio = { version = "*", features = ["full"] } tokio-util = { version = "0.7.19", features = ["rt"] } -uuid = { version = "1.26.0", features = ["v4", "v7"] } -thiserror = "2.0.20" +uuid = { version = "1.24.0", features = ["v4", "v7"] } +thiserror = "2.0.19" serde = { version = "1.0.229", features = ["derive"] } serde_json = "1.0.151" +zeroize = "1.9" diff --git a/README.md b/README.md index f60aecf..2c05741 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Omega The Omega is Tensamin's central Server. It maintains the centralized user Registry & manages Omikron useage. -On first start Omega creates an unencrypted private keyring at `./omega.mk` and the matching public bundle at `./omega.mpkb`. Existing identity files are loaded fail-closed, so a malformed keyring or mismatched public bundle stops startup. A missing public bundle is rebuilt from a valid private keyring without generating a new identity. +Omega requires the non-empty `OMEGA_IDENTITY_SECRET` environment variable at startup. On first start it creates the protected private identity at `./omega.mk` and the matching public bundle at `./omega.mpkb`. Existing identity files are loaded fail-closed, so a malformed keyring or mismatched public bundle stops startup. A missing public bundle is rebuilt from a valid private keyring without generating a new identity. ## MTP routing contract diff --git a/migrations/001_initial_schema.sql b/migrations/001_initial_schema.sql index 2e2da53..e2b8af5 100644 --- a/migrations/001_initial_schema.sql +++ b/migrations/001_initial_schema.sql @@ -15,7 +15,6 @@ CREATE TABLE IF NOT EXISTS users ( sub_end BIGINT NOT NULL DEFAULT 0, public_key BLOB NOT NULL, token BLOB NOT NULL, - created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), UNIQUE KEY uk_users_username (username), KEY idx_users_iota_id (iota_id), diff --git a/migrations/007_iota_snapshot_outbox.sql b/migrations/007_iota_snapshot_outbox.sql deleted file mode 100644 index ba9a972..0000000 --- a/migrations/007_iota_snapshot_outbox.sql +++ /dev/null @@ -1,4 +0,0 @@ -CREATE TABLE iota_snapshot_outbox ( - iota_id BIGINT NOT NULL PRIMARY KEY, - updated_at DATETIME NOT NULL -); diff --git a/migrations/008_user_created_at.sql b/migrations/008_user_created_at.sql deleted file mode 100644 index 99ccc2f..0000000 --- a/migrations/008_user_created_at.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE users - ADD COLUMN IF NOT EXISTS created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3); diff --git a/mtp-type-maps b/mtp-type-maps index f4e45aa..486541b 160000 --- a/mtp-type-maps +++ b/mtp-type-maps @@ -1 +1 @@ -Subproject commit f4e45aa3a3ad0e3c3a257f66857b904a1af7901c +Subproject commit 486541b9483356ff49ff3ec7016f87d3ecbeaa0e diff --git a/src/db/iota_repo.rs b/src/db/iota_repo.rs index 5b5101d..fb50432 100644 --- a/src/db/iota_repo.rs +++ b/src/db/iota_repo.rs @@ -22,7 +22,7 @@ pub async fn get_iota_by_id(id: IotaId) -> Result { pub async fn create_new_iota(public_key: PublicKeyBundle) -> Result { for _ in 0..16 { - let id = crate::db::user_repo::generate_protocol_id(); + let id = crate::db::user_repo::get_register_id().await?; let iota_id = IotaId::from(id.0); match register_complete_iota(iota_id, public_key.clone()).await { Ok(()) => return Ok(iota_id), diff --git a/src/db/user_repo.rs b/src/db/user_repo.rs index 950be67..0d10d4c 100644 --- a/src/db/user_repo.rs +++ b/src/db/user_repo.rs @@ -11,13 +11,22 @@ use std::collections::HashMap; pub const MAX_PROTOCOL_ID: i64 = (1_i64 << 48) - 1; const ID_ALLOCATION_ATTEMPTS: usize = 16; -pub fn generate_protocol_id() -> UserId { - loop { - let value = rand::random::() & ((1_u64 << 48) - 1); - if value != 0 { - return UserId::from(value as i64); - } +pub async fn get_register_id() -> Result { + use std::time::{SystemTime, UNIX_EPOCH}; + + let timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|d| d.as_secs()) + .unwrap_or(0); + + let ts = timestamp as i64; + if (1..=MAX_PROTOCOL_ID).contains(&ts) { + return Ok(UserId::from(ts)); } + + // Fall back to random if the timestamp is outside the 48-bit range. + let id = (rand::random::() & ((1_u64 << 48) - 1)) as i64; + Ok(UserId::from(id.max(1))) } pub fn valid_protocol_id(id: i64) -> bool { @@ -33,24 +42,8 @@ pub async fn allocate_registration(iota_id: IotaId, request_id: u32) -> Result<( )); } - let database = pool().await?; - sqlx::query( - "UPDATE registration_leases SET request_id = NULL \ - WHERE request_id IS NOT NULL AND expires_at < UTC_TIMESTAMP()", - ) - .execute(&database) - .await?; - for _ in 0..ID_ALLOCATION_ATTEMPTS { - let id = generate_protocol_id(); - let user_exists = sqlx::query("SELECT 1 FROM users WHERE id = ? LIMIT 1") - .bind(id.0) - .fetch_optional(&database) - .await? - .is_some(); - if user_exists { - continue; - } + let id = get_register_id().await?; let token = uuid::Uuid::new_v4().to_string(); let result = sqlx::query( "INSERT INTO registration_leases (token, user_id, iota_id, request_id, expires_at) \ @@ -60,7 +53,7 @@ pub async fn allocate_registration(iota_id: IotaId, request_id: u32) -> Result<( .bind(id.0) .bind(iota_id.0) .bind(request_id) - .execute(&database) + .execute(&pool().await?) .await; match result { Ok(_) => return Ok((id, token)), @@ -71,7 +64,7 @@ pub async fn allocate_registration(iota_id: IotaId, request_id: u32) -> Result<( ) .bind(iota_id.0) .bind(request_id) - .fetch_optional(&database) + .fetch_optional(&pool().await?) .await?; if let Some(existing) = existing { let current: i8 = existing.get("current"); @@ -101,9 +94,9 @@ pub(crate) fn is_duplicate_key(error: &sqlx::Error) -> bool { }) } -const USER_BY_USERNAME_QUERY: &str = "SELECT id, iota_id, username, display, status, presence_preference, about, avatar, sub_level, sub_end, public_key, token, CAST(UNIX_TIMESTAMP(created_at) * 1000 AS SIGNED) AS created_at FROM users WHERE username = ?"; -const USER_BY_ID_QUERY: &str = "SELECT id, iota_id, username, display, status, presence_preference, about, avatar, sub_level, sub_end, public_key, token, CAST(UNIX_TIMESTAMP(created_at) * 1000 AS SIGNED) AS created_at FROM users WHERE id = ?"; -const USER_COLUMNS: &str = "SELECT id, iota_id, username, display, status, presence_preference, about, avatar, sub_level, sub_end, public_key, token, CAST(UNIX_TIMESTAMP(created_at) * 1000 AS SIGNED) AS created_at FROM users"; +const USER_BY_USERNAME_QUERY: &str = "SELECT id, iota_id, username, display, status, presence_preference, about, avatar, sub_level, sub_end, public_key, token FROM users WHERE username = ?"; +const USER_BY_ID_QUERY: &str = "SELECT id, iota_id, username, display, status, presence_preference, about, avatar, sub_level, sub_end, public_key, token FROM users WHERE id = ?"; +const USER_COLUMNS: &str = "SELECT id, iota_id, username, display, status, presence_preference, about, avatar, sub_level, sub_end, public_key, token FROM users"; #[derive(FromRow)] struct UserRow { @@ -119,7 +112,6 @@ struct UserRow { sub_end: i64, public_key: Vec, token: Vec, - created_at: i64, } impl TryFrom for User { @@ -143,7 +135,6 @@ impl TryFrom for User { sub_end: row.sub_end, public_key, token: decode(row.token)?, - created_at: row.created_at, }) } } @@ -181,7 +172,7 @@ fn normalized_ids(ids: &[i64]) -> Vec { ids } -fn append_in_clause(query: &mut QueryBuilder, ids: &[i64]) { +fn append_in_clause(query: &mut QueryBuilder<'_, MySql>, ids: &[i64]) { query.push("("); for (index, id) in ids.iter().enumerate() { if index > 0 { @@ -192,7 +183,7 @@ fn append_in_clause(query: &mut QueryBuilder, ids: &[i64]) { query.push(")"); } -async fn fetch_users(mut query: QueryBuilder) -> Result> { +async fn fetch_users(mut query: QueryBuilder<'_, MySql>) -> Result> { query .build_query_as::() .fetch_all(&pool().await?) @@ -345,59 +336,21 @@ pub async fn change_status(id: UserId, value: String) -> Result<()> { } pub async fn change_iota_id(id: UserId, value: Option) -> Result<()> { - let mut transaction = pool().await?.begin().await?; - let previous = sqlx::query("SELECT iota_id FROM users WHERE id = ? FOR UPDATE") - .bind(id.0) - .fetch_optional(&mut *transaction) - .await? - .ok_or(OmegaError::NotFound)?; - let previous_iota_id: Option = previous.get("iota_id"); sqlx::query("UPDATE users SET iota_id = ? WHERE id = ?") .bind(value.map(|id| id.0)) .bind(id.0) - .execute(&mut *transaction) - .await?; - if let Some(iota_id) = previous_iota_id { - enqueue_iota_snapshot(&mut transaction, iota_id).await?; - } - if let Some(iota_id) = value { - enqueue_iota_snapshot(&mut transaction, iota_id.0).await?; - } - transaction.commit().await?; - Ok(()) -} - -async fn enqueue_iota_snapshot( - transaction: &mut sqlx::Transaction<'_, MySql>, - iota_id: i64, -) -> Result<()> { - sqlx::query( - "INSERT INTO iota_snapshot_outbox (iota_id, updated_at) VALUES (?, UTC_TIMESTAMP()) \ - ON DUPLICATE KEY UPDATE updated_at = VALUES(updated_at)", - ) - .bind(iota_id) - .execute(&mut **transaction) - .await?; - Ok(()) -} - -pub async fn pending_iota_snapshots() -> Result> { - let rows = sqlx::query("SELECT iota_id FROM iota_snapshot_outbox ORDER BY updated_at") - .fetch_all(&pool().await?) - .await?; - Ok(rows - .into_iter() - .map(|row| IotaId::from(row.get::("iota_id"))) - .collect()) -} - -pub async fn complete_iota_snapshot(iota_id: IotaId) -> Result<()> { - sqlx::query("DELETE FROM iota_snapshot_outbox WHERE iota_id = ?") - .bind(iota_id.0) .execute(&pool().await?) .await?; Ok(()) } +pub async fn change_token(id: UserId, value: String) -> Result<()> { + update( + id, + "UPDATE users SET token = ? WHERE id = ?", + value.into_bytes(), + ) + .await +} /// Delete the central identity while retaining a durable instruction for the /// last hosting Iota. The pending row is intentionally independent of users: /// it must outlive the account row. @@ -535,7 +488,6 @@ pub async fn register_complete_user( } }; result?; - enqueue_iota_snapshot(&mut transaction, iota_id.0).await?; sqlx::query("UPDATE registration_leases SET completed_at = UTC_TIMESTAMP() WHERE token = ?") .bind(®istration_token) .execute(&mut *transaction) @@ -546,20 +498,19 @@ pub async fn register_complete_user( fn valid_username(username: &str) -> bool { !username.is_empty() - && username.len() <= 15 - && username - .bytes() - .all(|byte| byte.is_ascii_lowercase() || byte.is_ascii_digit()) + && username.chars().count() <= 15 + && !username.chars().any(char::is_control) + && !username.contains(['/', '\\']) } #[cfg(test)] mod tests { - use super::{MAX_PROTOCOL_ID, generate_protocol_id, valid_protocol_id}; + use super::{MAX_PROTOCOL_ID, get_register_id, valid_protocol_id}; #[tokio::test] async fn generated_registration_ids_fit_the_mtp_wire_range() { for _ in 0..128 { - assert!(valid_protocol_id(generate_protocol_id().0)); + assert!(valid_protocol_id(get_register_id().await.unwrap().0)); } assert!(!valid_protocol_id(0)); assert!(valid_protocol_id(MAX_PROTOCOL_ID)); diff --git a/src/identity.rs b/src/identity.rs index c5d373a..a7a831d 100644 --- a/src/identity.rs +++ b/src/identity.rs @@ -1,7 +1,7 @@ use crate::error::{IdentityError, Result}; use mtp::crypto::{Keyring, PublicKeyBundle}; use mtp::files::{ - FileError, load_keyring_raw, load_public_key_bundle, save_keyring_raw, save_public_key_bundle, + FileError, load_keyring, load_public_key_bundle, save_keyring, save_public_key_bundle, }; use std::{ fs, @@ -22,20 +22,25 @@ pub struct OmegaIdentity { static PUBLIC_BUNDLE_TEMP_COUNTER: AtomicU64 = AtomicU64::new(0); impl OmegaIdentity { - pub fn load_or_create() -> Result { - Self::load_or_create_at(Path::new(KEYRING_PATH), Path::new(PUBLIC_KEY_PATH)) + pub fn load_or_create(passphrase: &[u8]) -> Result { + Self::load_or_create_at( + Path::new(KEYRING_PATH), + Path::new(PUBLIC_KEY_PATH), + passphrase, + ) } pub(crate) fn load_or_create_at( keyring_path: impl AsRef, public_key_path: impl AsRef, + passphrase: &[u8], ) -> Result { let keyring_path = keyring_path.as_ref(); let public_key_path = public_key_path.as_ref(); - let keyring = match load_keyring_raw(keyring_path) { + let keyring = match load_keyring(keyring_path, passphrase) { Ok(keyring) => keyring, Err(FileError::Io(error)) if error.kind() == std::io::ErrorKind::NotFound => { - return Self::create_at(keyring_path, public_key_path); + return Self::create_at(keyring_path, public_key_path, passphrase); } Err(error) => { return Err(IdentityError::Storage { @@ -67,11 +72,13 @@ impl OmegaIdentity { Ok(identity) } - fn create_at(keyring_path: &Path, public_key_path: &Path) -> Result { + fn create_at(keyring_path: &Path, public_key_path: &Path, passphrase: &[u8]) -> Result { let keyring = Keyring::generate(); - save_keyring_raw(&keyring, keyring_path).map_err(|error| IdentityError::Storage { - path: keyring_path.to_path_buf(), - source: error, + save_keyring(&keyring, keyring_path, passphrase).map_err(|error| { + IdentityError::Storage { + path: keyring_path.to_path_buf(), + source: error, + } })?; Self::persist_public_bundle(&keyring.public_key_bundle(), public_key_path)?; Ok(Self { @@ -181,7 +188,9 @@ mod tests { let directory = test_directory(); let keyring_path = directory.join("omega.mk"); let public_key_path = directory.join("omega.mpkb"); - let first = OmegaIdentity::load_or_create_at(&keyring_path, &public_key_path) + let passphrase = b"test-passphrase"; + + let first = OmegaIdentity::load_or_create_at(&keyring_path, &public_key_path, passphrase) .expect("create identity"); let first_bundle = first.public_key_bundle().try_as_bytes().expect("bundle"); @@ -190,8 +199,9 @@ mod tests { assert_eq!(&keyring_bytes[..4], b"MTMK"); assert_eq!(&bundle_bytes[..4], b"MPKB"); - let restarted = OmegaIdentity::load_or_create_at(&keyring_path, &public_key_path) - .expect("reload identity"); + let restarted = + OmegaIdentity::load_or_create_at(&keyring_path, &public_key_path, passphrase) + .expect("reload identity"); assert_eq!( restarted .public_key_bundle() @@ -217,7 +227,8 @@ mod tests { let public_key_path = directory.join("omega.mpkb"); fs::write(&keyring_path, b"not-a-keyring").expect("write invalid keyring"); - let result = OmegaIdentity::load_or_create_at(&keyring_path, &public_key_path); + let result = + OmegaIdentity::load_or_create_at(&keyring_path, &public_key_path, b"test-passphrase"); assert!(result.is_err()); assert!(!public_key_path.exists()); @@ -229,12 +240,16 @@ mod tests { let directory = test_directory(); let keyring_path = directory.join("omega.mk"); let public_key_path = directory.join("omega.mpkb"); - OmegaIdentity::load_or_create_at(&keyring_path, &public_key_path).expect("create identity"); + let passphrase = b"test-passphrase"; + + OmegaIdentity::load_or_create_at(&keyring_path, &public_key_path, passphrase) + .expect("create identity"); let original_keyring = fs::read(&keyring_path).expect("read keyring"); fs::remove_file(&public_key_path).expect("remove bundle"); - let repaired = OmegaIdentity::load_or_create_at(&keyring_path, &public_key_path) - .expect("repair bundle"); + let repaired = + OmegaIdentity::load_or_create_at(&keyring_path, &public_key_path, passphrase) + .expect("repair bundle"); assert_eq!( fs::read(&keyring_path).expect("read keyring"), @@ -256,13 +271,16 @@ mod tests { let directory = test_directory(); let keyring_path = directory.join("omega.mk"); let public_key_path = directory.join("omega.mpkb"); - OmegaIdentity::load_or_create_at(&keyring_path, &public_key_path).expect("create identity"); + let passphrase = b"test-passphrase"; + + OmegaIdentity::load_or_create_at(&keyring_path, &public_key_path, passphrase) + .expect("create identity"); let other_keyring = Keyring::generate(); save_public_key_bundle(&other_keyring.public_key_bundle(), &public_key_path) .expect("save mismatched bundle"); assert!(matches!( - OmegaIdentity::load_or_create_at(&keyring_path, &public_key_path), + OmegaIdentity::load_or_create_at(&keyring_path, &public_key_path, passphrase), Err(crate::OmegaError::Identity( IdentityError::PublicBundleMismatch { .. } )) @@ -272,25 +290,24 @@ mod tests { } #[test] - fn existing_raw_keyring_is_reloaded_without_a_passphrase() { + fn wrong_passphrase_does_not_replace_existing_keyring() { let directory = test_directory(); let keyring_path = directory.join("omega.mk"); let public_key_path = directory.join("omega.mpkb"); + let passphrase = b"test-passphrase"; - let first = OmegaIdentity::load_or_create_at(&keyring_path, &public_key_path) + OmegaIdentity::load_or_create_at(&keyring_path, &public_key_path, passphrase) .expect("create identity"); let original_keyring = fs::read(&keyring_path).expect("read keyring"); - let reloaded = OmegaIdentity::load_or_create_at(&keyring_path, &public_key_path) - .expect("reload identity"); + assert!( + OmegaIdentity::load_or_create_at(&keyring_path, &public_key_path, b"wrong-passphrase") + .is_err() + ); assert_eq!( fs::read(&keyring_path).expect("read keyring"), original_keyring ); - assert_eq!( - reloaded.public_key_bundle().try_as_bytes().expect("bundle"), - first.public_key_bundle().try_as_bytes().expect("bundle") - ); fs::remove_dir_all(directory).expect("remove test directory"); } diff --git a/src/main.rs b/src/main.rs index e2915c7..e04238c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,7 +15,6 @@ pub use error::{OmegaError, Result}; use crate::db::initialize; use crate::state::OmegaState; use crate::transport::omikron_connection; -use crate::transport::omikron_manager; use crate::util::file_util::get_directory; use crate::util::logger::PrintType; use crate::util::logger::startup; @@ -26,6 +25,7 @@ use std::env; use std::path::Path; use std::time::Duration; use tokio::time::interval; +use zeroize::Zeroizing; #[tokio::main] async fn main() { @@ -38,6 +38,18 @@ async fn main() { log_in!("Incoming messages"); log_out!("Outgoing messages"); + let identity_secret = match env::var("OMEGA_IDENTITY_SECRET") { + Ok(secret) if !secret.is_empty() => secret, + Ok(_) => { + log!("[FATAL] OMEGA_IDENTITY_SECRET must not be empty"); + return; + } + Err(error) => { + log!("[FATAL] Unable to load OMEGA_IDENTITY_SECRET: {}", error); + return; + } + }; + let identity_secret = Zeroizing::new(identity_secret); let config = match OmegaConfig::from_env() { Ok(config) => config, Err(error) => { @@ -49,13 +61,14 @@ async fn main() { log!("[FATAL] Omega rate-limit configuration was initialized more than once"); return; } - let identity = match identity::OmegaIdentity::load_or_create() { + let identity = match identity::OmegaIdentity::load_or_create(identity_secret.as_bytes()) { Ok(identity) => identity, Err(error) => { log!("[FATAL] Omega identity initialization failed: {}", error); return; } }; + drop(identity_secret); let state = OmegaState::new(identity, config); log!("Started"); @@ -84,7 +97,6 @@ async fn main() { } } }); - let snapshot_outbox_worker = omikron_manager::spawn_iota_snapshot_outbox_worker(); let port: u16 = env::var("PORT") .ok() .and_then(|s| s.parse().ok()) @@ -102,5 +114,4 @@ async fn main() { } rate_limit_cleanup.abort(); short_link_cleanup.abort(); - snapshot_outbox_worker.abort(); } diff --git a/src/models/user.rs b/src/models/user.rs index 41d132b..a9595d9 100644 --- a/src/models/user.rs +++ b/src/models/user.rs @@ -17,5 +17,4 @@ pub struct User { pub public_key: PublicKeyBundle, #[serde(skip_serializing)] pub token: String, - pub created_at: i64, } diff --git a/src/server/api.rs b/src/server/api.rs index 12ac33e..41e5d22 100644 --- a/src/server/api.rs +++ b/src/server/api.rs @@ -13,7 +13,6 @@ use crate::models::UserId; use crate::server::{ middleware, validation::{parse_positive_id, validate_non_empty}, - with_cors, }; use crate::transport::omikron_manager::{ get_all_connections, get_connected_omikron, get_iota_primary_omikron_connection, @@ -194,34 +193,39 @@ pub async fn handle( let method = request.method; let path = request.uri.path().to_string(); if method != Method::OPTIONS && !middleware::allow(request.remote_addr.ip(), &path) { - return with_cors(response.status(StatusCode::TOO_MANY_REQUESTS).body(json( - &StatusResponse { + return response + .status(StatusCode::TOO_MANY_REQUESTS) + .header("access-control-allow-origin", crate::config::cors_origin()) + .body(json(&StatusResponse { status: "error_rate_limited", - }, - ))); + })); } if method == Method::OPTIONS { - return with_cors(response.status(StatusCode::NO_CONTENT)); + return response + .status(StatusCode::OK) + .header("access-control-allow-origin", crate::config::cors_origin()) + .header("access-control-allow-methods", "GET, POST, OPTIONS") + .header("access-control-allow-headers", "*"); } let path_parts: Vec<&str> = path.split('/').filter(|part| !part.is_empty()).collect(); if let ["api", "download", "iota_frontend"] = path_parts.as_slice() { let file_path = format!("{}/downloads/iota_frontend.zip", get_directory()); return match std::fs::read(file_path) { - Ok(bytes) => with_cors( - response - .status(StatusCode::OK) - .header("content-type", "application/zip") - .header( - "content-disposition", - "attachment; filename=\"iota_frontend.zip\"", - ) - .body(Bytes::from(bytes)), - ), - Err(_) => with_cors(response.status(StatusCode::NOT_FOUND).body(json( - &StatusResponse { + Ok(bytes) => response + .status(StatusCode::OK) + .header("access-control-allow-origin", crate::config::cors_origin()) + .header("content-type", "application/zip") + .header( + "content-disposition", + "attachment; filename=\"iota_frontend.zip\"", + ) + .body(Bytes::from(bytes)), + Err(_) => response + .status(StatusCode::NOT_FOUND) + .header("access-control-allow-origin", crate::config::cors_origin()) + .body(json(&StatusResponse { status: "error_not_found", - }, - ))), + })), }; } if let ["direct", short @ ..] = path_parts.as_slice() { @@ -229,16 +233,19 @@ pub async fn handle( let location = crate::server::short_link::get_short_link(&short) .await .unwrap_or_else(|_| "https://tensamin.net".to_string()); - return with_cors( - response - .status(StatusCode::TEMPORARY_REDIRECT) - .header("location", &location), - ); + return response + .status(StatusCode::TEMPORARY_REDIRECT) + .header("location", &location); } let (status, body) = route(&path_parts, &identity) .await .unwrap_or_else(|error| (error.status_code(), error_body(&error))); - with_cors(response.status(status).body(body)) + response + .status(status) + .header("access-control-allow-origin", crate::config::cors_origin()) + .header("access-control-allow-headers", "*") + .header("access-control-allow-methods", "GET, POST, OPTIONS") + .body(body) } pub async fn handle_pattern( diff --git a/src/server/index.rs b/src/server/index.rs index 0f3aa5e..f128c30 100644 --- a/src/server/index.rs +++ b/src/server/index.rs @@ -1,8 +1,6 @@ use http::StatusCode; use mtp::webserver::HttpResponse; -use crate::server::with_cors; - pub fn index_handler(response: HttpResponse) -> HttpResponse { let documentation = r#" Omega API Server @@ -35,10 +33,8 @@ live in-memory state; it is empty after an Omega restart until Omikrons sync. All other routes will return this documentation. "#; - with_cors( - response - .status(StatusCode::OK) - .header("content-type", "text/plain; charset=utf-8") - .body(documentation), - ) + response + .status(StatusCode::OK) + .header("content-type", "text/plain; charset=utf-8") + .body(documentation) } diff --git a/src/server/mod.rs b/src/server/mod.rs index c3acd11..64df9e9 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -4,29 +4,3 @@ pub mod middleware; pub mod short_link; pub mod validation; pub mod web; - -use mtp::webserver::HttpResponse; - -pub(crate) fn with_cors(response: HttpResponse) -> HttpResponse { - response - .header("access-control-allow-origin", crate::config::cors_origin()) - .header("access-control-allow-methods", "GET, POST, OPTIONS") - .header("access-control-allow-headers", "*") -} - -#[cfg(test)] -mod tests { - use super::with_cors; - use mtp::webserver::HttpResponse; - - #[test] - fn web_responses_allow_all_origins() { - let response = with_cors(HttpResponse::default()); - assert_eq!(response.headers["access-control-allow-origin"], "*"); - assert_eq!( - response.headers["access-control-allow-methods"], - "GET, POST, OPTIONS" - ); - assert_eq!(response.headers["access-control-allow-headers"], "*"); - } -} diff --git a/src/server/web.rs b/src/server/web.rs index a8219a1..5975888 100644 --- a/src/server/web.rs +++ b/src/server/web.rs @@ -1,6 +1,5 @@ use crate::identity::OmegaIdentity; use crate::server::{api, index::index_handler}; -use http::{Method, StatusCode}; use mtp::webserver::{HttpRequest, HttpResponse, RouteParams, WebServerConfig}; use std::{future::Future, pin::Pin, sync::Arc}; @@ -44,11 +43,5 @@ pub fn build_web_config( )? .route_pattern("/api/get/user/{id}", api_pattern_handler(identity.clone()))? .route_pattern("/direct/{short}", api_pattern_handler(identity))? - .fallback(|request, response| async move { - if request.method == Method::OPTIONS { - crate::server::with_cors(response.status(StatusCode::NO_CONTENT)) - } else { - index_handler(response) - } - }) + .fallback(|_request, response| async move { index_handler(response) }) } diff --git a/src/transport/connection.rs b/src/transport/connection.rs index 92644dc..66d9eb3 100644 --- a/src/transport/connection.rs +++ b/src/transport/connection.rs @@ -73,6 +73,7 @@ pub(crate) fn validate_dispatch_fields(value: &CommunicationValue) -> OmikronRes message_type, mtp::codec::CommunicationType::GetUserData | mtp::codec::CommunicationType::ChangeUserData + | mtp::codec::CommunicationType::ChangeIotaData | mtp::codec::CommunicationType::DeleteUser | mtp::codec::CommunicationType::AttachUserBegin | mtp::codec::CommunicationType::AttachUserComplete @@ -146,6 +147,7 @@ mod tests { for message_type in [ CommunicationType::GetUserData, CommunicationType::ChangeUserData, + CommunicationType::ChangeIotaData, CommunicationType::DeleteUser, CommunicationType::AttachUserBegin, CommunicationType::AttachUserComplete, diff --git a/src/transport/handlers/account.rs b/src/transport/handlers/account.rs index 9de94a5..ae7215f 100644 --- a/src/transport/handlers/account.rs +++ b/src/transport/handlers/account.rs @@ -84,7 +84,7 @@ pub async fn release_from_iota( match user_repo::change_iota_id(user.id, None).await { Ok(()) => { if let Some(iota) = previous_iota { - let _ = crate::transport::omikron_manager::publish_iota_user_snapshot(iota.0).await; + crate::transport::omikron_manager::publish_iota_user_snapshot(iota.0).await; } connection .send( @@ -225,9 +225,9 @@ pub async fn attach_complete( match user_repo::change_iota_id(user.id, Some(IotaId::from(requester))).await { Ok(()) => { if let Some(iota) = previous_iota.filter(|id| id.0 != requester) { - let _ = crate::transport::omikron_manager::publish_iota_user_snapshot(iota.0).await; + crate::transport::omikron_manager::publish_iota_user_snapshot(iota.0).await; } - let _ = crate::transport::omikron_manager::publish_iota_user_snapshot(requester).await; + crate::transport::omikron_manager::publish_iota_user_snapshot(requester).await; connection .send( &CommunicationValue::new(CommunicationType::Success) @@ -252,8 +252,7 @@ async fn complete_delete( Ok(iota_id) => { let cleanup_pending = iota_id.is_some(); if let Some(iota_id) = iota_id { - let _ = - crate::transport::omikron_manager::publish_iota_user_snapshot(iota_id.0).await; + crate::transport::omikron_manager::publish_iota_user_snapshot(iota_id.0).await; crate::transport::omikron_manager::deliver_pending_erasures(iota_id.0).await; } connection diff --git a/src/transport/handlers/presence.rs b/src/transport/handlers/presence.rs index 048dadf..a958a92 100644 --- a/src/transport/handlers/presence.rs +++ b/src/transport/handlers/presence.rs @@ -562,15 +562,6 @@ pub async fn sync_status( &affected_iota_ids.iter().copied().collect::>(), ) .await?; - if sessions.iter().any(|(user_id, _, iota_id)| { - !users - .iter() - .any(|user| user.id.0 == *user_id && user.iota_id == Some(IotaId::from(*iota_id))) - }) { - return connection - .send_error_response(request_id, CommunicationType::ErrorInvalidData) - .await; - } let user_ids = users.iter().map(|user| user.id.0).collect::>(); apply_preferences( &state, diff --git a/src/transport/handlers/register.rs b/src/transport/handlers/register.rs index c8582e3..2c2501a 100644 --- a/src/transport/handlers/register.rs +++ b/src/transport/handlers/register.rs @@ -135,17 +135,6 @@ pub async fn complete_user( .await { Ok(()) => { - if let Err(error) = - crate::transport::omikron_manager::publish_iota_user_snapshot(iota_id).await - { - crate::log_in!( - crate::util::logger::PrintType::General, - "Could not publish Iota snapshot after registering user {} on Iota {}: {}", - user_id, - iota_id, - error - ); - } connection .send( &CommunicationValue::new(CommunicationType::Success) diff --git a/src/transport/handlers/user_data.rs b/src/transport/handlers/user_data.rs index 21a607c..e6b676a 100644 --- a/src/transport/handlers/user_data.rs +++ b/src/transport/handlers/user_data.rs @@ -64,10 +64,6 @@ pub async fn get_user( DataValue::Str(user.public_key.try_to_base64()?), ) .add_typed_default(DataType::UserId, DataValue::SignedNumber(id.into())) - .add_typed_default( - DataType::CreatedAt, - DataValue::SignedNumber(user.created_at.into()), - ) .add_typed_default(DataType::Display, DataValue::Str(display)) .add_typed_default( DataType::SubLevel, @@ -268,3 +264,49 @@ pub async fn change_user( ) -> OmikronResult<()> { update_user(connection, value).await } + +pub async fn change_iota( + connection: Arc, + value: CommunicationValue, +) -> OmikronResult<()> { + let request_id = value.require_id()?; + let reset_data = value.get_data(DataType::ResetToken); + let Some(reset) = reset_data.as_str() else { + return connection + .send_error_response(value.require_id()?, CommunicationType::ErrorInvalidData) + .await; + }; + let new_token_data = value.get_data(DataType::NewToken); + let Some(new_token) = new_token_data.as_str() else { + return connection + .send_error_response(value.require_id()?, CommunicationType::ErrorInvalidData) + .await; + }; + let user_id = UserId::from(value.require_sender_i64()?); + let user = match user_repo::get_by_user_id(user_id).await { + Ok(user) => user, + Err(_) => { + return connection + .send_error_response(request_id, CommunicationType::ErrorNotFound) + .await; + } + }; + if user.token != reset { + return connection + .send_error_response(request_id, CommunicationType::ErrorInvalidChallenge) + .await; + } + let result = + match user_repo::change_iota_id(user_id, Some(IotaId::from(value.require_sender_i64()?))) + .await + { + Ok(()) => user_repo::change_token(user_id, new_token.to_owned()).await, + Err(error) => Err(error), + }; + let response = match result { + Ok(()) => CommunicationValue::new(CommunicationType::Success), + Err(error) => CommunicationValue::new(CommunicationType::ErrorInternal) + .add_typed_default(DataType::ErrorType, DataValue::Str(error.to_string())), + }; + connection.send(&response.with_id(request_id)).await +} diff --git a/src/transport/omikron_connection.rs b/src/transport/omikron_connection.rs index b4be514..8c536c7 100644 --- a/src/transport/omikron_connection.rs +++ b/src/transport/omikron_connection.rs @@ -394,7 +394,9 @@ impl OmikronConnection { let request_id = value.id(); let result = crate::transport::relay_router::route_from_omikron(id, value).await; let response = match &result { - Ok(response) => request_id.map(|request_id| response.clone().with_id(request_id)), + Ok(()) => request_id.map(|request_id| { + CommunicationValue::new(CommunicationType::Success).with_id(request_id) + }), Err(error) => { log_err!(id, PrintType::Omega, "Relay routing failed: {}", error); request_id.map(|request_id| { @@ -415,9 +417,7 @@ impl OmikronConnection { send_error ); } - return result - .map(|_| ()) - .map_err(|error| crate::error::OmegaError::Transport(error.to_string())); + return result.map_err(|error| crate::error::OmegaError::Transport(error.to_string())); } match value.get_comm_type_enum() { Some(CommunicationType::ShortenLink) => { @@ -449,6 +449,9 @@ impl OmikronConnection { Some(CommunicationType::ChangeUserData) => { crate::transport::handlers::user_data::change_user(self, value).await } + Some(CommunicationType::ChangeIotaData) => { + crate::transport::handlers::user_data::change_iota(self, value).await + } Some(CommunicationType::GetRegister) => { crate::transport::handlers::register::get_register(self, value).await } @@ -761,9 +764,8 @@ pub async fn start(port: u16, state: Arc) -> Result<(), Box>> = Lazy::new(DashMap::new); @@ -26,7 +23,6 @@ pub async fn add_omikron(conn: Arc) { if let Some(old) = OMIKRON_CONNECTIONS.insert(id, conn.clone()) { old.close().await; } - let _ = flush_iota_snapshot_outbox().await; } pub async fn remove_omikron(omikron_id: i64, connection: &Arc) -> bool { @@ -67,18 +63,12 @@ pub async fn get_all_connections() .await .map_err(|_| ())?; for user in users { - if let Some(iota_id) = user.iota_id { - for omikron_id in state - .presence - .iota_connections(iota_id.0) - .unwrap_or_default() + for route in state.presence.routes_for_user(user.id.0) { + if let Some(iotas) = result.get_mut(&route.omikron_id) + && let Some(iota_id) = user.iota_id + && let Some(users) = iotas.get_mut(&iota_id.0) { - if let Some(users) = result - .get_mut(&omikron_id) - .and_then(|iotas| iotas.get_mut(&iota_id.0)) - { - users.push(user.id.0); - } + users.push(user.id.0); } } } @@ -125,18 +115,19 @@ pub async fn send_to_user(user_id: i64, cv: &CommunicationValue) { } } -/* - * Publish each Iota membership snapshot to every live relay route. Each - * Omikron keeps a local authorization index, so sending only a primary route - * leaves the remaining relays stale after registration or migration. - */ -pub async fn publish_iota_user_snapshot(iota_id: i64) -> OmikronResult<()> { - let state = get_state().ok_or(crate::error::OmegaError::NotConnected)?; - let omikron_ids = state - .presence - .iota_connections(iota_id) - .ok_or(crate::error::OmegaError::NotConnected)?; - let users = user_repo::get_users_by_iota_id(crate::models::IotaId::from(iota_id)).await?; +/// Publish the authoritative membership list after an attach, migration, or +/// release. Omikron replaces its full local index from this snapshot. +pub async fn publish_iota_user_snapshot(iota_id: i64) { + let Some(omikron_id) = get_iota_primary_omikron_connection(iota_id) else { + return; + }; + let Some(connection) = get_connected_omikron(omikron_id) else { + return; + }; + let Ok(users) = user_repo::get_users_by_iota_id(crate::models::IotaId::from(iota_id)).await + else { + return; + }; let user_ids = users .into_iter() .map(|user| DataValue::SignedNumber(user.id.0.into())) @@ -144,54 +135,7 @@ pub async fn publish_iota_user_snapshot(iota_id: i64) -> OmikronResult<()> { let snapshot = CommunicationValue::new(CommunicationType::IotaUserData) .add_typed_default(DataType::IotaId, DataValue::SignedNumber(iota_id.into())) .add_typed_default(DataType::UserIds, DataValue::Array(user_ids)); - for omikron_id in omikron_ids { - let connection = - get_connected_omikron(omikron_id).ok_or(crate::error::OmegaError::NotConnected)?; - connection.send(&snapshot).await?; - } - Ok(()) -} - -pub async fn flush_iota_snapshot_outbox() -> OmikronResult<()> { - for iota_id in user_repo::pending_iota_snapshots().await? { - match publish_iota_user_snapshot(iota_id.0).await { - Ok(()) => { - if let Err(error) = user_repo::complete_iota_snapshot(iota_id).await { - crate::log_in!( - crate::util::logger::PrintType::General, - "Could not complete Iota snapshot outbox entry for {}: {}", - iota_id.0, - error - ); - } - } - Err(error) => { - crate::log_in!( - crate::util::logger::PrintType::General, - "Could not publish Iota snapshot for {}: {}", - iota_id.0, - error - ); - } - } - } - Ok(()) -} - -pub fn spawn_iota_snapshot_outbox_worker() -> JoinHandle<()> { - tokio::spawn(async { - let mut retry = interval(Duration::from_secs(30)); - loop { - retry.tick().await; - if let Err(error) = flush_iota_snapshot_outbox().await { - crate::log_in!( - crate::util::logger::PrintType::General, - "Could not load Iota snapshot outbox: {}", - error - ); - } - } - }) + let _ = connection.send(&snapshot).await; } pub async fn deliver_pending_erasures(iota_id: i64) { diff --git a/src/transport/relay_router.rs b/src/transport/relay_router.rs index 0a76ea7..0cbd1f2 100644 --- a/src/transport/relay_router.rs +++ b/src/transport/relay_router.rs @@ -82,7 +82,7 @@ pub fn error_response_type(error: &RelayRouteError) -> CommunicationType { pub async fn route_from_omikron( source_omikron_id: i64, frame: CommunicationValue, -) -> Result { +) -> Result<(), RelayRouteError> { let frame = ensure_relay_frame_id(frame); if !frame.is_type(CommunicationType::Relay) { return Err(RelayRouteError::Relay(RelayError::NotRelay)); @@ -154,7 +154,7 @@ pub async fn route_from_omikron( RelayRouteError::Send(error.to_string()) })?; if response.is_type(CommunicationType::Success) { - Ok(response) + Ok(()) } else { Err(RelayRouteError::Send(format!( "destination Omikron rejected the Relay with {}",