From 0e71e9dc5f7f9bd2bbb2ad652aee53dda31d57bb Mon Sep 17 00:00:00 2001 From: sam Date: Tue, 16 Jan 2024 22:20:19 +0100 Subject: [PATCH] add initial chat server stuff --- Cargo.lock | 496 +++++++++++++++++- chat/Cargo.toml | 16 + chat/migrations/20240116203402_init.sql | 51 ++ config.chat.toml | 3 + config.identity.toml | 1 + foxchat/Cargo.toml | 1 + foxchat/src/error/mod.rs | 7 + foxchat/src/http/response.rs | 20 + foxchat/src/lib.rs | 5 +- identity/Cargo.toml | 7 +- identity/migrations/20240116172715_tokens.sql | 4 + identity/src/config.rs | 9 + identity/src/db/account.rs | 83 +++ identity/src/db/mod.rs | 6 +- identity/src/http/account/create_user.rs | 18 +- identity/src/model/account.rs | 3 +- identity/src/model/chat_instance.rs | 30 +- identity/src/model/instance.rs | 2 + 18 files changed, 722 insertions(+), 40 deletions(-) create mode 100644 chat/migrations/20240116203402_init.sql create mode 100644 config.chat.toml create mode 100644 foxchat/src/error/mod.rs create mode 100644 identity/migrations/20240116172715_tokens.sql create mode 100644 identity/src/db/account.rs diff --git a/Cargo.lock b/Cargo.lock index e216280..8380dd5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -30,6 +30,21 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + [[package]] name = "allocator-api2" version = "0.2.16" @@ -99,6 +114,20 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "async-compression" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc2d0cfb2a7388d34f590e76686704c494ed7aaceed62ee1ba35cbf363abc2a5" +dependencies = [ + "brotli", + "flate2", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", +] + [[package]] name = "async-trait" version = "0.1.77" @@ -153,10 +182,10 @@ dependencies = [ "base64", "bytes", "futures-util", - "http", - "http-body", + "http 1.0.0", + "http-body 1.0.0", "http-body-util", - "hyper", + "hyper 1.1.0", "hyper-util", "itoa", "matchit", @@ -188,8 +217,8 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http", - "http-body", + "http 1.0.0", + "http-body 1.0.0", "http-body-util", "mime", "pin-project-lite", @@ -286,6 +315,27 @@ dependencies = [ "cipher", ] +[[package]] +name = "brotli" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "516074a47ef4bce09577a3b379392300159ce5b1ba2e501ff1c819950066100f" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "2.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + [[package]] name = "bumpalo" version = "3.14.0" @@ -322,6 +372,24 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chat" version = "0.1.0" +dependencies = [ + "axum", + "clap", + "color-eyre", + "eyre", + "foxchat", + "rand", + "rsa", + "serde", + "serde_json", + "sqlx", + "tokio", + "toml", + "tower-http", + "tracing", + "tracing-subscriber", + "ulid", +] [[package]] name = "chrono" @@ -424,6 +492,16 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.6" @@ -454,6 +532,15 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + [[package]] name = "crossbeam-queue" version = "0.3.11" @@ -523,6 +610,15 @@ dependencies = [ "serde", ] +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -578,6 +674,16 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6" +[[package]] +name = "flate2" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "flume" version = "0.11.0" @@ -595,6 +701,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -613,6 +734,7 @@ dependencies = [ "serde", "serde_json", "sqlx", + "thiserror", "tracing", "uuid", ] @@ -716,6 +838,25 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +[[package]] +name = "h2" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b553656127a00601c8ae5590fcfdc118e4083a7924b6cf4ffc1ea4b99dc429d7" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.11", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "h2" version = "0.4.1" @@ -727,7 +868,7 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http", + "http 1.0.0", "indexmap", "slab", "tokio", @@ -802,6 +943,17 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "http" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http" version = "1.0.0" @@ -813,6 +965,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.11", + "pin-project-lite", +] + [[package]] name = "http-body" version = "1.0.0" @@ -820,7 +983,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" dependencies = [ "bytes", - "http", + "http 1.0.0", ] [[package]] @@ -831,8 +994,8 @@ checksum = "41cb79eb393015dadd30fc252023adb0b2400a0caee0fa2a077e6e21a551e840" dependencies = [ "bytes", "futures-util", - "http", - "http-body", + "http 1.0.0", + "http-body 1.0.0", "pin-project-lite", ] @@ -848,6 +1011,30 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "hyper" +version = "0.14.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.3.23", + "http 0.2.11", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + [[package]] name = "hyper" version = "1.1.0" @@ -857,9 +1044,9 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "h2", - "http", - "http-body", + "h2 0.4.1", + "http 1.0.0", + "http-body 1.0.0", "httparse", "httpdate", "itoa", @@ -867,6 +1054,19 @@ dependencies = [ "tokio", ] +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper 0.14.28", + "native-tls", + "tokio", + "tokio-native-tls", +] + [[package]] name = "hyper-util" version = "0.1.2" @@ -876,9 +1076,9 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http", - "http-body", - "hyper", + "http 1.0.0", + "http-body 1.0.0", + "hyper 1.1.0", "pin-project-lite", "socket2", "tokio", @@ -913,16 +1113,18 @@ name = "identity" version = "0.1.0" dependencies = [ "axum", + "base64", "bcrypt", "clap", "color-eyre", - "dotenvy", "eyre", "foxchat", "rand", + "reqwest", "rsa", "serde", "serde_json", + "sha256", "sqlx", "tokio", "toml", @@ -930,7 +1132,6 @@ dependencies = [ "tracing", "tracing-subscriber", "ulid", - "uuid", ] [[package]] @@ -968,6 +1169,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + [[package]] name = "itertools" version = "0.12.0" @@ -1074,6 +1281,16 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1100,6 +1317,24 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "nix" version = "0.27.1" @@ -1205,6 +1440,50 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "openssl" +version = "0.10.62" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671" +dependencies = [ + "bitflags 2.4.1", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "overload" version = "0.1.1" @@ -1383,6 +1662,47 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "reqwest" +version = "0.11.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" +dependencies = [ + "async-compression", + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2 0.3.23", + "http 0.2.11", + "http-body 0.4.6", + "hyper 0.14.28", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "mime_guess", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "system-configuration", + "tokio", + "tokio-native-tls", + "tokio-util", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + [[package]] name = "ring" version = "0.17.7" @@ -1480,6 +1800,15 @@ version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -1496,6 +1825,29 @@ dependencies = [ "untrusted", ] +[[package]] +name = "security-framework" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "serde" version = "1.0.195" @@ -1580,6 +1932,19 @@ dependencies = [ "digest", ] +[[package]] +name = "sha256" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18278f6a914fa3070aa316493f7d2ddfb9ac86ebc06fa3b83bffda487e9065b0" +dependencies = [ + "async-trait", + "bytes", + "hex", + "sha2", + "tokio", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -1714,7 +2079,6 @@ dependencies = [ "tokio-stream", "tracing", "url", - "uuid", "webpki-roots", ] @@ -1798,7 +2162,6 @@ dependencies = [ "stringprep", "thiserror", "tracing", - "uuid", "whoami", ] @@ -1839,7 +2202,6 @@ dependencies = [ "stringprep", "thiserror", "tracing", - "uuid", "whoami", ] @@ -1865,7 +2227,6 @@ dependencies = [ "tracing", "url", "urlencoding", - "uuid", ] [[package]] @@ -1919,6 +2280,27 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tempfile" version = "3.9.0" @@ -2005,6 +2387,16 @@ dependencies = [ "syn 2.0.48", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-stream" version = "0.1.14" @@ -2100,8 +2492,8 @@ checksum = "0da193277a4e2c33e59e09b5861580c33dd0a637c3883d0fa74ba40c0374af2e" dependencies = [ "bitflags 2.4.1", "bytes", - "http", - "http-body", + "http 1.0.0", + "http-body 1.0.0", "http-body-util", "pin-project-lite", "tower-layer", @@ -2189,6 +2581,12 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + [[package]] name = "tungstenite" version = "0.21.0" @@ -2198,7 +2596,7 @@ dependencies = [ "byteorder", "bytes", "data-encoding", - "http", + "http 1.0.0", "httparse", "log", "rand", @@ -2224,6 +2622,15 @@ dependencies = [ "serde", ] +[[package]] +name = "unicase" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] + [[package]] name = "unicode-bidi" version = "0.3.14" @@ -2320,6 +2727,15 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -2351,6 +2767,18 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bde2032aeb86bdfaecc8b261eef3cba735cc426c1f3a3416d1e0791be95fc461" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.90" @@ -2380,6 +2808,16 @@ version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" +[[package]] +name = "web-sys" +version = "0.3.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "webpki-roots" version = "0.25.3" @@ -2564,6 +3002,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "zerocopy" version = "0.7.32" diff --git a/chat/Cargo.toml b/chat/Cargo.toml index a90a88e..8ff139e 100644 --- a/chat/Cargo.toml +++ b/chat/Cargo.toml @@ -6,3 +6,19 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +foxchat = { path = "../foxchat" } +axum = { version = "0.7.4", features = ["macros", "query", "tracing", "ws"] } +clap = { version = "4.4.16", features = ["env", "derive"] } +sqlx = { version = "0.7.3", features = ["runtime-tokio", "tls-rustls", "postgres", "migrate", "chrono", "json"] } +serde = { version = "1.0.195", features = ["derive"] } +serde_json = "1.0.111" +ulid = { version = "1.1.0", features = ["serde"] } +eyre = "0.6.11" +color-eyre = "0.6.2" +rsa = { version = "0.9.6", features = ["serde", "sha2"] } +rand = "0.8.5" +toml = "0.8.8" +tokio = { version = "1.35.1", features = ["macros", "rt-multi-thread"] } +tracing-subscriber = "0.3.18" +tracing = "0.1.40" +tower-http = { version = "0.5.1", features = ["trace"] } diff --git a/chat/migrations/20240116203402_init.sql b/chat/migrations/20240116203402_init.sql new file mode 100644 index 0000000..2178b46 --- /dev/null +++ b/chat/migrations/20240116203402_init.sql @@ -0,0 +1,51 @@ +create type instance_status as enum ('active', 'suspended'); + +create table identity_instances ( + id text primary key, + domain text not null unique, + base_url text not null, + public_key text not null, + status instance_status not null default 'active', + reason text +); + +create table users ( + id text primary key, + instance_id text not null references identity_instances (id) on delete cascade, + remote_user_id text not null, + username text not null, + + avatar text -- URL, not hash, as this is a remote file +); + +create unique index users_remote_user_id_idx on users (instance_id, remote_user_id); +create unique index users_username_idx on users (instance_id, lower(username)); + +create table guilds ( + id text primary key, + owner_id text not null references users (id) on delete cascade, + name text not null +); + +create table guilds_users ( + guild_id text not null references guilds (id) on delete cascade, + user_id text not null references users (id) on delete cascade, + + primary key (guild_id, user_id) +); + +create table channels ( + id text primary key, + guild_id text not null references guilds (id) on delete cascade, + name text not null, + topic text +); + +create table messages ( + id text primary key, + channel_id text not null references channels (id) on delete cascade, + author_id text not null, + updated_at timestamptz not null default now(), + + content text not null +); diff --git a/config.chat.toml b/config.chat.toml new file mode 100644 index 0000000..81b22c0 --- /dev/null +++ b/config.chat.toml @@ -0,0 +1,3 @@ +database_url = "postgresql://foxchat:password@localhost/foxchat_chat_dev" +port = 3001 +log_level = "DEBUG" diff --git a/config.identity.toml b/config.identity.toml index 0522bd2..d46cc32 100644 --- a/config.identity.toml +++ b/config.identity.toml @@ -1,3 +1,4 @@ database_url = "postgresql://foxchat:password@localhost/foxchat_ident_dev" port = 3000 log_level = "DEBUG" +insecure_requests = true diff --git a/foxchat/Cargo.toml b/foxchat/Cargo.toml index d57e354..23943b5 100644 --- a/foxchat/Cargo.toml +++ b/foxchat/Cargo.toml @@ -11,5 +11,6 @@ eyre = "0.6.11" serde = { version = "1.0.195", features = ["derive"] } serde_json = "1.0.111" sqlx = "0.7.3" +thiserror = "1.0.56" tracing = "0.1.40" uuid = { version = "1.6.1", features = ["v7"] } diff --git a/foxchat/src/error/mod.rs b/foxchat/src/error/mod.rs new file mode 100644 index 0000000..3240f94 --- /dev/null +++ b/foxchat/src/error/mod.rs @@ -0,0 +1,7 @@ +use thiserror::Error; + +#[derive(Error, Debug, Copy, Clone)] +pub enum QueryError { + #[error("object not found")] + NotFound, +} diff --git a/foxchat/src/http/response.rs b/foxchat/src/http/response.rs index 33054c7..3abd596 100644 --- a/foxchat/src/http/response.rs +++ b/foxchat/src/http/response.rs @@ -8,6 +8,8 @@ use serde::Serialize; use serde_json::json; use tracing::error; +use crate::QueryError; + pub struct ApiError { status: StatusCode, code: ErrorCode, @@ -32,6 +34,7 @@ impl IntoResponse for ApiError { #[serde(rename_all = "SCREAMING_SNAKE_CASE")] pub enum ErrorCode { InternalServerError, + ObjectNotFound, } impl From for ApiError { @@ -50,6 +53,11 @@ impl From for ApiError { impl From for ApiError { fn from(err: Report) -> Self { + match err.downcast_ref::() { + Some(e) => return (*e).into(), + None => {} + }; + error!("Error in handler: {}", err); ApiError { status: StatusCode::INTERNAL_SERVER_ERROR, @@ -62,3 +70,15 @@ impl From for ApiError { } } } + +impl From for ApiError { + fn from(err: QueryError) -> Self { + match err { + QueryError::NotFound => ApiError { + status: StatusCode::NOT_FOUND, + code: ErrorCode::ObjectNotFound, + message: "Object not found".into(), + }, + } + } +} diff --git a/foxchat/src/lib.rs b/foxchat/src/lib.rs index 4ead4eb..bfab54c 100644 --- a/foxchat/src/lib.rs +++ b/foxchat/src/lib.rs @@ -1,2 +1,5 @@ -pub mod s2s; +pub mod error; pub mod http; +pub mod s2s; + +pub use error::QueryError; diff --git a/identity/Cargo.toml b/identity/Cargo.toml index 05c3d91..5c97009 100644 --- a/identity/Cargo.toml +++ b/identity/Cargo.toml @@ -9,9 +9,7 @@ edition = "2021" foxchat = { path = "../foxchat" } axum = { version = "0.7.4", features = ["macros", "query", "tracing", "ws"] } clap = { version = "4.4.16", features = ["env", "derive"] } -dotenvy = "0.15.7" -sqlx = { version = "0.7.3", features = ["runtime-tokio", "tls-rustls", "postgres", "migrate", "uuid", "chrono", "json"] } -uuid = { version = "1.6.1", features = ["v7"] } +sqlx = { version = "0.7.3", features = ["runtime-tokio", "tls-rustls", "postgres", "migrate", "chrono", "json"] } serde = { version = "1.0.195", features = ["derive"] } serde_json = "1.0.111" ulid = { version = "1.1.0", features = ["serde"] } @@ -25,3 +23,6 @@ tracing-subscriber = "0.3.18" tracing = "0.1.40" tower-http = { version = "0.5.1", features = ["trace"] } bcrypt = "0.15.0" +base64 = "0.21.7" +sha256 = "1.5.0" +reqwest = { version = "0.11.23", features = ["json", "gzip", "brotli", "multipart"] } diff --git a/identity/migrations/20240116172715_tokens.sql b/identity/migrations/20240116172715_tokens.sql new file mode 100644 index 0000000..10f885c --- /dev/null +++ b/identity/migrations/20240116172715_tokens.sql @@ -0,0 +1,4 @@ +create table tokens ( + token text primary key, + account_id text not null references accounts (id) on delete cascade +); diff --git a/identity/src/config.rs b/identity/src/config.rs index fcdd23b..8f7a7bd 100644 --- a/identity/src/config.rs +++ b/identity/src/config.rs @@ -12,6 +12,9 @@ pub struct Config { pub port: u16, pub auto_migrate: Option, pub log_level: Option, + /// Whether to try HTTP if a server is not served over HTTPS. + /// This should only be set to `true` in development. + pub insecure_requests: Option, } impl Config { @@ -40,6 +43,11 @@ impl Config { _ => None } } + + /// If true, the server will try to identify remote servers over HTTP if they are not available over HTTPS + pub fn should_try_insecure(&self) -> bool { + self.insecure_requests.unwrap_or(false) + } } impl Default for Config { @@ -49,6 +57,7 @@ impl Default for Config { port: 3000, auto_migrate: None, log_level: None, + insecure_requests: None, } } } diff --git a/identity/src/db/account.rs b/identity/src/db/account.rs new file mode 100644 index 0000000..5c0a428 --- /dev/null +++ b/identity/src/db/account.rs @@ -0,0 +1,83 @@ +use base64::prelude::{Engine, BASE64_URL_SAFE_NO_PAD}; +use eyre::{Context, Report, Result}; +use foxchat::QueryError; +use rand::RngCore; +use sqlx::{PgExecutor, Pool, Postgres}; + +use crate::model::account::{Account, Role}; + +pub async fn get_user_by_username_and_password( + pool: Pool, + username: String, + password: String, +) -> Result { + let account = sqlx::query_as!( + Account, + r#"select + id, username, email, password, role as "role: Role", avatar + from accounts where username = $1"#, + username + ) + .fetch_one(&pool) + .await + .map_err(|e| -> Report { + match e { + sqlx::Error::RowNotFound => QueryError::NotFound.into(), + _ => e.into(), + } + })?; + + if bcrypt::verify(password, &account.password).map_err(|_| QueryError::NotFound)? { + return Ok(account); + } + + Err(QueryError::NotFound.into()) +} + +pub async fn check_token(pool: &Pool, token: String) -> Result { + let hash = sha256::digest(token); + + let account = sqlx::query_as!( + Account, + r#"select + a.id, a.username, a.email, a.password, a.role as "role: Role", a.avatar + from accounts a + join tokens t on t.account_id = a.id + where t.account_id = $1"#, + hash + ) + .fetch_optional(pool) + .await?; + + match account { + Some(a) => Ok(a), + None => Err(QueryError::NotFound.into()), + } +} + +pub async fn create_token(executor: impl PgExecutor<'_>, user_id: &String) -> Result { + let token = generate_token_string(); + let hash = sha256::digest(&token); + sqlx::query!( + "insert into tokens (token, account_id) values ($1, $2)", + hash, + user_id + ) + .execute(executor) + .await?; + + Ok(token) +} + +fn generate_token_string() -> String { + let mut data = [0u8; 32]; + rand::thread_rng().fill_bytes(&mut data); + + BASE64_URL_SAFE_NO_PAD.encode(data) +} + +const PASSWORD_COST: u32 = 12; + +pub fn hash_password(password: String) -> Result { + bcrypt::hash(password, PASSWORD_COST).wrap_err("failed to hash password") +} diff --git a/identity/src/db/mod.rs b/identity/src/db/mod.rs index aa3cddb..e5d6e49 100644 --- a/identity/src/db/mod.rs +++ b/identity/src/db/mod.rs @@ -1,5 +1,9 @@ +pub mod account; + +pub use account::{check_token, create_token, get_user_by_username_and_password}; + use eyre::{OptionExt, Result}; -use rsa::pkcs1::{LineEnding, EncodeRsaPublicKey, EncodeRsaPrivateKey}; +use rsa::pkcs1::{EncodeRsaPrivateKey, EncodeRsaPublicKey, LineEnding}; use rsa::{RsaPrivateKey, RsaPublicKey}; use sqlx::postgres::PgPoolOptions; use sqlx::{Pool, Postgres}; diff --git a/identity/src/http/account/create_user.rs b/identity/src/http/account/create_user.rs index 89e2360..919e28b 100644 --- a/identity/src/http/account/create_user.rs +++ b/identity/src/http/account/create_user.rs @@ -1,31 +1,34 @@ use std::sync::Arc; use axum::{Extension, Json}; -use eyre::{Context, Result}; +use eyre::Result; use foxchat::http::ApiError; use serde::{Deserialize, Serialize}; use ulid::Ulid; -use crate::app_state::AppState; - -const PASSWORD_COST: u32 = 12; +use crate::{app_state::AppState, db::{create_token, account::hash_password}}; pub async fn create_user( Extension(state): Extension>, Json(data): Json, ) -> Result, ApiError> { - let password_hash = - bcrypt::hash(data.password, PASSWORD_COST).wrap_err("failed to hash password")?; + let mut tx = state.pool.begin().await?; + + let password_hash = hash_password(data.password)?; let account = sqlx::query!( "insert into accounts (id, username, email, password) values ($1, $2, $3, $4) returning id, username", Ulid::new().to_string(), data.username, data.email, password_hash) - .fetch_one(&state.pool) + .fetch_one(&mut *tx) .await?; + let token = create_token(&mut *tx, &account.id).await?; + + tx.commit().await?; Ok(Json(CreateUserResponse { id: account.id, username: account.username, + token, })) } @@ -40,4 +43,5 @@ pub struct CreateUserRequest { pub struct CreateUserResponse { pub id: String, pub username: String, + pub token: String, } diff --git a/identity/src/model/account.rs b/identity/src/model/account.rs index de1d25b..86b6554 100644 --- a/identity/src/model/account.rs +++ b/identity/src/model/account.rs @@ -1,8 +1,7 @@ use serde::{Deserialize, Serialize}; -use ulid::Ulid; pub struct Account { - pub id: Ulid, + pub id: String, pub username: String, pub email: String, pub password: String, diff --git a/identity/src/model/chat_instance.rs b/identity/src/model/chat_instance.rs index 6ec5dd3..1692ee7 100644 --- a/identity/src/model/chat_instance.rs +++ b/identity/src/model/chat_instance.rs @@ -1,8 +1,12 @@ +use std::sync::Arc; + +use eyre::Result; use serde::{Deserialize, Serialize}; -use ulid::Ulid; + +use crate::app_state::AppState; pub struct ChatInstance { - pub id: Ulid, + pub id: String, pub domain: String, pub base_url: String, pub public_key: String, @@ -16,3 +20,25 @@ pub enum InstanceStatus { Active, Suspended, } + +impl ChatInstance { + pub async fn get(state: Arc, domain: String) -> Result { + if let Some(instance) = sqlx::query_as!( + Self, + r#"select id, domain, base_url, public_key, + status as "status: InstanceStatus", reason + from chat_instances where domain = $1"#, + domain + ) + .fetch_optional(&state.pool) + .await? + { + return Ok(instance); + } + + // TODO: identify server process + // only try HTTP if `state.config.should_try_insecure()` + + todo!() + } +} diff --git a/identity/src/model/instance.rs b/identity/src/model/instance.rs index 42fd39c..0b2553d 100644 --- a/identity/src/model/instance.rs +++ b/identity/src/model/instance.rs @@ -9,6 +9,8 @@ pub struct Instance { } impl Instance { + /// Gets the instance's configuration. + /// This is a singleton row that is always present. pub async fn get(pool: &Pool) -> Result { let instance = sqlx::query!("SELECT * FROM instance WHERE id = 1") .fetch_one(pool)