From 4785ab529c0dc36ac7b7eed80a2d9dd3337108b7 Mon Sep 17 00:00:00 2001 From: javalsai Date: Thu, 26 Mar 2026 21:56:23 +0100 Subject: [PATCH] feat: lay the ground for login --- Cargo.lock | 584 ++++++++++++++++++++++++++++++++++- Cargo.toml | 3 +- src/consts.rs | 6 + src/db/mod.rs | 32 ++ src/main.rs | 32 +- src/server/caches/mod.rs | 65 ++-- src/server/mod.rs | 9 +- src/server/services/login.rs | 54 ++++ src/server/services/mod.rs | 1 + src/utils.rs | 54 ++++ 10 files changed, 801 insertions(+), 39 deletions(-) create mode 100644 src/db/mod.rs create mode 100644 src/server/services/login.rs diff --git a/Cargo.lock b/Cargo.lock index 67b57a2..d666f54 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -49,7 +49,7 @@ dependencies = [ "mime", "percent-encoding", "pin-project-lite", - "rand", + "rand 0.9.2", "sha1", "smallvec", "tokio", @@ -282,12 +282,30 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + [[package]] name = "bindgen" version = "0.69.5" @@ -323,6 +341,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-buffer" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be" +dependencies = [ + "hybrid-array", +] + [[package]] name = "brotli" version = "8.0.2" @@ -392,6 +419,16 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common 0.1.7", + "inout", +] + [[package]] name = "clang-sys" version = "1.8.1" @@ -471,9 +508,21 @@ version = "0.1.0" source = "git+https://git.javalsai.tuxcord.net/rust/const-macros.git#6f91e1e1a6ce2e5cdb5866a1e1d7841ed5e3cd6f" dependencies = [ "magic", - "sha2", + "sha2 0.10.9", ] +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + [[package]] name = "const-str" version = "1.1.0" @@ -523,6 +572,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + [[package]] name = "crc32fast" version = "1.5.0" @@ -556,6 +614,18 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.7" @@ -566,6 +636,25 @@ dependencies = [ "typenum", ] +[[package]] +name = "crypto-common" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77727bb15fa921304124b128af125e7e3b968275d1b108b379190264f4423710" +dependencies = [ + "hybrid-array", +] + +[[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", + "zeroize", +] + [[package]] name = "deranged" version = "0.5.8" @@ -604,8 +693,21 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", - "crypto-common", + "block-buffer 0.10.4", + "const-oid 0.9.6", + "crypto-common 0.1.7", + "subtle", +] + +[[package]] +name = "digest" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4850db49bf08e663084f7fb5c87d202ef91a3907271aff24a94eb97ff039153c" +dependencies = [ + "block-buffer 0.12.0", + "const-oid 0.10.2", + "crypto-common 0.2.1", ] [[package]] @@ -619,12 +721,45 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest 0.10.7", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + [[package]] name = "either" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest 0.10.7", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + [[package]] name = "encoding_rs" version = "0.8.35" @@ -634,6 +769,29 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "env_filter" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e90c2accc4b07a8456ea0debdc2e7587bdd890680d71173a15d4ae604f6eef" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0621c04f2196ac3f488dd583365b9c09be011a4ab8b9f37248ffcc8f6198b56a" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "jiff", + "log", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -671,6 +829,16 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "find-msvc-tools" version = "0.1.9" @@ -758,6 +926,18 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", ] [[package]] @@ -791,6 +971,17 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "h2" version = "0.3.27" @@ -831,6 +1022,15 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[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]] name = "http" version = "0.2.12" @@ -854,6 +1054,15 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "hybrid-array" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8655f91cd07f2b9d0c24137bd650fe69617773435ee5ec83022377777ce65ef1" +dependencies = [ + "typenum", +] + [[package]] name = "icu_collections" version = "2.1.1" @@ -980,6 +1189,15 @@ dependencies = [ "serde_core", ] +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.2" @@ -1001,6 +1219,30 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +[[package]] +name = "jiff" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a3546dc96b6d42c5f24902af9e2538e82e39ad350b0c766eb3fbf2d8f3d8359" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", +] + +[[package]] +name = "jiff-static" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a8c8b344124222efd714b73bb41f8b5120b27a7cc1c75593a6ff768d9d05aa4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "jobserver" version = "0.1.34" @@ -1032,6 +1274,9 @@ name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] [[package]] name = "lazycell" @@ -1051,6 +1296,12 @@ version = "0.2.183" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + [[package]] name = "litemap" version = "0.8.1" @@ -1179,10 +1430,56 @@ dependencies = [ ] [[package]] -name = "num-conv" -version = "0.2.0" +name = "num-bigint-dig" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" +checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" +dependencies = [ + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand 0.8.5", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-conv" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] [[package]] name = "oidc" @@ -1194,13 +1491,16 @@ dependencies = [ "clap", "const-macros", "const-str", + "env_logger", "futures-util", "libc", + "log", "magic", "moka", "pamsock", "serde", - "sha2", + "sha2 0.11.0", + "ssh-key", "thiserror", "tokio", "toml", @@ -1219,6 +1519,44 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2 0.10.9", +] + +[[package]] +name = "p384" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2 0.10.9", +] + +[[package]] +name = "p521" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc9e2161f1f215afdfce23677034ae137bbd45016a880c2eb3ba8eb95f085b2" +dependencies = [ + "base16ct", + "ecdsa", + "elliptic-curve", + "primeorder", + "rand_core 0.6.4", + "sha2 0.10.9", +] + [[package]] name = "pam" version = "0.8.0" @@ -1293,6 +1631,15 @@ dependencies = [ "windows-link", ] +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.2" @@ -1305,6 +1652,27 @@ 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", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + [[package]] name = "pkg-config" version = "0.3.32" @@ -1317,6 +1685,15 @@ version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" +[[package]] +name = "portable-atomic-util" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "091397be61a01d4be58e7841595bd4bfedb15f1cd54977d79b8271e94ed799a3" +dependencies = [ + "portable-atomic", +] + [[package]] name = "potential_utf" version = "0.1.4" @@ -1351,6 +1728,15 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + [[package]] name = "proc-macro2" version = "1.0.106" @@ -1381,14 +1767,34 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + [[package]] name = "rand" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ - "rand_chacha", - "rand_core", + "rand_chacha 0.9.0", + "rand_core 0.9.5", +] + +[[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]] @@ -1398,7 +1804,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", ] [[package]] @@ -1454,6 +1869,37 @@ version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[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", + "rand_core 0.6.4", + "sha2 0.10.9", + "signature", + "spki", + "subtle", + "zeroize", +] + [[package]] name = "rustc-hash" version = "1.1.0" @@ -1487,6 +1933,20 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + [[package]] name = "semver" version = "1.0.27" @@ -1564,8 +2024,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", - "cpufeatures", - "digest", + "cpufeatures 0.2.17", + "digest 0.10.7", ] [[package]] @@ -1575,8 +2035,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", - "cpufeatures", - "digest", + "cpufeatures 0.2.17", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "digest 0.11.2", ] [[package]] @@ -1595,6 +2066,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 = "simd-adler32" version = "0.3.8" @@ -1633,6 +2114,63 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "ssh-cipher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caac132742f0d33c3af65bfcde7f6aa8f62f0e991d80db99149eb9d44708784f" +dependencies = [ + "cipher", + "ssh-encoding", +] + +[[package]] +name = "ssh-encoding" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9242b9ef4108a78e8cd1a2c98e193ef372437f8c22be363075233321dd4a15" +dependencies = [ + "base64ct", + "pem-rfc7468", + "sha2 0.10.9", +] + +[[package]] +name = "ssh-key" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b86f5297f0f04d08cabaa0f6bff7cb6aec4d9c3b49d87990d63da9d9156a8c3" +dependencies = [ + "p256", + "p384", + "p521", + "rand_core 0.6.4", + "rsa", + "sec1", + "sha2 0.10.9", + "signature", + "ssh-cipher", + "ssh-encoding", + "subtle", + "zeroize", +] + [[package]] name = "stable_deref_trait" version = "1.2.1" @@ -1645,6 +2183,12 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" version = "1.0.109" @@ -1871,9 +2415,9 @@ checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-segmentation" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da36089a805484bcccfffe0739803392c8298778a2d2f09febf76fac5ad9025b" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" [[package]] name = "unicode-xid" @@ -2303,6 +2847,12 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + [[package]] name = "zerotrie" version = "0.2.3" diff --git a/Cargo.toml b/Cargo.toml index 7f22ad5..e649e1b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,7 +46,8 @@ log = { version = "0.4.29", optional = true } magic = "0.16" moka = { version = "0.12", features = ["async-lock", "future"] } serde = { version = "1.0", features = ["derive"] } -sha2 = "0.10" +sha2 = "0.11" +ssh-key = "0.6.7" thiserror = "2.0" tokio = { version = "1.49", features = ["full"] } toml = "1.0" diff --git a/src/consts.rs b/src/consts.rs index 39a3185..1477115 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -37,6 +37,11 @@ pub const USER_PFP_PATHS: &[&str] = &[ ".face", ]; +/// Path relative to a user's home to ssh's `.authorized_keys`, used to verify user's identity when +/// distrusted +pub const AUTHORIZED_KEYS_PATH: &str = ".ssh/authorized_keys"; +pub const MAX_AUTHORIZED_KEYS_SIZE: u64 = 64 * 1024; // 64 KiB + pub const ERROR_ASCII_ARTS: &[&str] = &[ include_str!("../assets/lain/lain-dancing.txt"), include_str!("../assets/lain/lain-head.txt"), @@ -51,4 +56,5 @@ pub mod mime { pub mod web_scopes { pub const IMAGES: &str = "/image"; + pub const LOGIN: &str = "/login"; } diff --git a/src/db/mod.rs b/src/db/mod.rs new file mode 100644 index 0000000..33535f4 --- /dev/null +++ b/src/db/mod.rs @@ -0,0 +1,32 @@ +//! Everything that interacts with DB (or generally persistent state) +//! +//! Currently it's all in memory or with default values until I model all the data properly and +//! choose a DB provider. + +use std::collections::HashSet; + +use tokio::sync::RwLock; + +pub struct DB { + // TODO: should I use some sort of UIDs instead of the username everywhere? + enabled_users: RwLock>, +} + +impl DB { + #[must_use] + #[expect(clippy::new_without_default)] + pub fn new() -> Self { + Self { + enabled_users: RwLock::new(HashSet::new()), + } + } + + pub async fn enable_user(&mut self, user: String) { + self.enabled_users.write().await.insert(user); + } + + #[must_use] + pub async fn user_is_enabled(&self, user: &str) -> bool { + self.enabled_users.read().await.contains(user) + } +} diff --git a/src/main.rs b/src/main.rs index a4abafc..35fa6fa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ #![feature( decl_macro, duration_constructors, + iterator_try_collect, never_type, once_cell_try, seek_stream_len @@ -26,6 +27,34 @@ //! //! I will try to keep those 3 modules as documented as possible, please feel free to open any //! issues/PRs regarding information in there. +//! +//! # Public Information +//! +//! To make sure this application doesn't expose any public imformation it's important to define +//! what public information we are willing to expose. The application deals with user information +//! so it must leak at least some information, to make sure we don't overreach, we must have clear +//! where we draw the line. +//! +//! By default all information is private, but this application might leak by default: +//! +//! - **User system information:** Unix's UID of a given username. +//! - **User profile pictures:** See [`consts::USER_PFP_PATHS`]. +//! - **User's `autorized_ssh_keys`:** See [`consts::AUTHORIZED_KEYS_PATH`]. +//! +//! Note that no file information within user's home can be accessed until the user adds `o+x` +//! permissions on their home directory. Once this is done, only state of files regarding the +//! previous can be publicly accessible, there's no arbirtary path reading. +//! +//! Any user information is checked ASAP against the allowed groups (see [`conf::Unix::groups`]) to +//! fail fast without exposing any personal information for users alien to these groups. That means +//! that any reference to the "user", will assume its already from an allowed group, if its not a +//! group member, it will be treated as nonexistent. +//! +//! Information about existance of a user alien to the configured groups might vulnerable to timing +//! attacks though. +//! +//! TODO: This was clearly defined after some API was already written so these assumptions will +//! need to be reviewed for the old code (notably pfp logic). use std::fs::File; @@ -37,6 +66,7 @@ pub mod args; pub mod auth; pub mod conf; pub mod consts; +pub mod db; pub mod ext; pub mod serdes; pub mod server; @@ -61,7 +91,7 @@ async fn main() -> anyhow::Result<()> { // (idek japanese but im vibing) println!("\n\x1b[1;3;4;33mConfiguration\x1b[0m: {conf:#?}\n"); - server::start_app(args, conf).await?; + server::start_app(args, conf, db::DB::new()).await?; Ok(()) } diff --git a/src/server/caches/mod.rs b/src/server/caches/mod.rs index dd43183..dae1d32 100644 --- a/src/server/caches/mod.rs +++ b/src/server/caches/mod.rs @@ -1,13 +1,14 @@ -use std::{ffi::OsStr, io, os::unix::fs::MetadataExt, path::Path, sync::Arc}; +use std::{ffi::OsStr, io, path::Path, sync::Arc}; use ::users::os::unix::UserExt as _; use moka::future::{Cache, CacheBuilder}; use sha2::Digest as _; -use tokio::{fs::File, io::AsyncReadExt}; +use ssh_key::PublicKey; use crate::{ consts, server::caches::{mime::Mime, users::UsersCache}, + utils::fs::{read_limited_path, read_limited_path_str}, }; pub mod mime; @@ -39,6 +40,8 @@ pub struct AppCache<'a> { pfp_cache: Cache>, } +/// Most of the methods in here fail silently with file i/o and some might cache results, I chose +/// either arbitrarily on the spot but I should make this more explicit. impl<'a> AppCache<'a> { /// # Errors /// @@ -97,23 +100,6 @@ impl<'a> AppCache<'a> { } async fn read_logo_from_home(&self, home: &Path) -> Option { - async fn read_limited_path(path: &Path) -> io::Result> { - let f = File::open(path).await?; - let size = f.metadata().await?.size(); - - if size > MAXSIZE { - return Err(io::Error::new( - io::ErrorKind::FileTooLarge, - "filesize is bigger than MAXSIZE", - )); - } - let mut buf = Vec::with_capacity(size.try_into().expect("u64 to fit in usize")); - // `.take()` just in case an open fd happens to grow, `.metadata()` SHOULD take - // properties from the fd and lock the read fd until its closed but still - f.take(MAXSIZE).read_to_end(&mut buf).await?; - Ok(buf) - } - for subpath in consts::USER_PFP_PATHS { let path = home.join(subpath); @@ -183,4 +169,45 @@ impl<'a> AppCache<'a> { Some(img) } + + /// Gets [`consts::AUTHORIZED_KEYS_PATH`] from user's home. + /// + /// # Errors + /// + /// In any of [`GetAuthorizedKeysError`] + pub async fn get_authorized_keys( + &self, + username: &str, + ) -> Result, GetAuthorizedKeysError> { + async fn read_users_authorized_keys( + home_dir: &Path, + ) -> Result, GetAuthorizedKeysError> { + let buf = read_limited_path_str::<{ consts::MAX_AUTHORIZED_KEYS_SIZE }>( + &home_dir.join(consts::AUTHORIZED_KEYS_PATH), + ) + .await + .map_err(GetAuthorizedKeysError::ReadSshFile)?; + + Ok(buf.lines().map(PublicKey::from_openssh).try_collect()?) + } + + let user = self + .get_member_user_by_name(username) + .await + .ok_or(GetAuthorizedKeysError::UserNotFound)?; + + Ok(read_users_authorized_keys(user.home_dir()) + .await? + .into_boxed_slice()) + } +} + +#[derive(Debug, thiserror::Error)] +pub enum GetAuthorizedKeysError { + #[error("said user was not found")] + UserNotFound, + #[error("failure reading ssh file: {0}")] + ReadSshFile(io::Error), + #[error("error parsing ssh key: {0}")] + ParseError(#[from] ssh_key::Error), } diff --git a/src/server/mod.rs b/src/server/mod.rs index ec907bd..e35db0e 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -10,6 +10,7 @@ pub struct AppState { pub args: crate::args::Args, pub config: &'static crate::conf::Config, pub cache: caches::AppCache<'static>, + pub db: crate::db::DB, } /// Type alias to be used just as `data: AppData,` extractor in [`actix_web`] request handlers. @@ -17,7 +18,11 @@ pub type AppData = static_app_data::StaticAppDataExtractor<&'static AppState>; /// Leaks memory for the sake of not atomic'ing all over. #[expect(clippy::missing_errors_doc)] -pub async fn start_app(args: crate::args::Args, config: crate::conf::Config) -> io::Result<()> { +pub async fn start_app( + args: crate::args::Args, + config: crate::conf::Config, + db: crate::db::DB, +) -> io::Result<()> { use crate::consts::web_scopes as ws; let config = Box::leak(Box::new(config)); @@ -28,6 +33,7 @@ pub async fn start_app(args: crate::args::Args, config: crate::conf::Config) -> args, config, cache, + db, })); println!( @@ -41,6 +47,7 @@ pub async fn start_app(args: crate::args::Args, config: crate::conf::Config) -> .wrap(middleware::Logger::new("%a (%{r}a) %r -> %s, %b B in %T s")) .wrap(middleware::NormalizePath::trim()) .service(services::images::make_scope(ws::IMAGES)) + .service(services::login::make_scope(ws::LOGIN)) .default_service(web::to(services::not_found::not_found)) }) .bind(&app.config.server.listen)? diff --git a/src/server/services/login.rs b/src/server/services/login.rs new file mode 100644 index 0000000..7c380c4 --- /dev/null +++ b/src/server/services/login.rs @@ -0,0 +1,54 @@ +use actix_web::{ + HttpResponse, get, post, + web::{self, Path}, +}; + +use crate::{consts, server::AppData}; + +#[must_use] +pub fn make_scope(path: &str) -> actix_web::Scope { + web::scope(path) + .service(login) + .service(login_post) + .service(activate) +} + +#[get("")] +pub async fn login(_data: AppData) -> HttpResponse { + HttpResponse::Ok().content_type(consts::mime::HTML).body( + "\ +
\ +

Username

\ + \ +

Password

\ + \ + \ +
\ + ", + ) +} + +#[post("")] +pub async fn login_post(_data: AppData) -> HttpResponse { + HttpResponse::NotImplemented() + .content_type(consts::mime::HTML) + .body( + "\ +

TODO

\ + ", + ) +} + +#[get("/activate/{username}")] +pub async fn activate(data: AppData, username: Path) -> HttpResponse { + let keys = data.cache.get_authorized_keys(&username).await; + + match keys { + Ok(keys) => HttpResponse::Ok() + .content_type(consts::mime::TEXT) + .body(format!("{keys:#?}")), + Err(err) => HttpResponse::BadRequest() + .content_type(consts::mime::TEXT) + .body(err.to_string()), + } +} diff --git a/src/server/services/mod.rs b/src/server/services/mod.rs index e42e914..d73a75d 100644 --- a/src/server/services/mod.rs +++ b/src/server/services/mod.rs @@ -1,2 +1,3 @@ pub mod images; pub mod not_found; +pub mod login; diff --git a/src/utils.rs b/src/utils.rs index 8b425b2..9dbb60e 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -83,3 +83,57 @@ pub mod shasum { } } } + +pub mod fs { + use std::{io, os::unix::fs::MetadataExt as _, path::Path}; + + use tokio::{fs::File, io::AsyncReadExt}; + + /// # Errors + /// + /// On any underlaying file i/o error. + /// + /// # Panics + /// + /// If a file's [`u64`] doesn't fit in memory. + pub async fn read_limited_path(path: &Path) -> io::Result> { + let f = File::open(path).await?; + let size = f.metadata().await?.size(); + + if size > MAXSIZE { + return Err(io::Error::new( + io::ErrorKind::FileTooLarge, + "filesize is bigger than MAXSIZE", + )); + } + let mut buf = Vec::with_capacity(size.try_into().expect("u64 to fit in usize")); + // `.take()` just in case an open fd happens to grow, `.metadata()` SHOULD take + // properties from the fd and lock the read fd until its closed but still + f.take(MAXSIZE).read_to_end(&mut buf).await?; + Ok(buf) + } + + /// # Errors + /// + /// On any underlaying file i/o error. + /// + /// # Panics + /// + /// If a file's [`u64`] doesn't fit in memory. + pub async fn read_limited_path_str(path: &Path) -> io::Result { + let f = File::open(path).await?; + let size = f.metadata().await?.size(); + + if size > MAXSIZE { + return Err(io::Error::new( + io::ErrorKind::FileTooLarge, + "filesize is bigger than MAXSIZE", + )); + } + let mut buf = String::with_capacity(size.try_into().expect("u64 to fit in usize")); + // `.take()` just in case an open fd happens to grow, `.metadata()` SHOULD take + // properties from the fd and lock the read fd until its closed but still + f.take(MAXSIZE).read_to_string(&mut buf).await?; + Ok(buf) + } +}