Init
This commit is contained in:
378
Cargo.lock
generated
378
Cargo.lock
generated
@@ -93,6 +93,18 @@ dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "argon2"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"blake2",
|
||||
"cpufeatures",
|
||||
"password-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.6"
|
||||
@@ -280,9 +292,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "axum"
|
||||
version = "0.8.7"
|
||||
version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b098575ebe77cb6d14fc7f32749631a6e44edbef6b796f89b020e99ba20d425"
|
||||
checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8"
|
||||
dependencies = [
|
||||
"axum-core",
|
||||
"axum-macros",
|
||||
@@ -345,6 +357,12 @@ dependencies = [
|
||||
"syn 2.0.110",
|
||||
]
|
||||
|
||||
[[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"
|
||||
@@ -392,6 +410,15 @@ dependencies = [
|
||||
"wyz",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "blake2"
|
||||
version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
|
||||
dependencies = [
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
@@ -501,9 +528,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.42"
|
||||
version = "0.4.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"
|
||||
checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118"
|
||||
dependencies = [
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
@@ -513,6 +540,16 @@ dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cipher"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
|
||||
dependencies = [
|
||||
"crypto-common",
|
||||
"inout",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.53"
|
||||
@@ -619,6 +656,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"
|
||||
@@ -629,6 +678,32 @@ dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "curve25519-dalek"
|
||||
version = "4.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"curve25519-dalek-derive",
|
||||
"digest",
|
||||
"fiat-crypto",
|
||||
"rustc_version",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "curve25519-dalek-derive"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.110",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.20.11"
|
||||
@@ -741,6 +816,41 @@ version = "0.15.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
|
||||
|
||||
[[package]]
|
||||
name = "ecdsa"
|
||||
version = "0.16.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca"
|
||||
dependencies = [
|
||||
"der",
|
||||
"digest",
|
||||
"elliptic-curve",
|
||||
"rfc6979",
|
||||
"signature",
|
||||
"spki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ed25519"
|
||||
version = "2.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
|
||||
dependencies = [
|
||||
"signature",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ed25519-dalek"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9"
|
||||
dependencies = [
|
||||
"curve25519-dalek",
|
||||
"ed25519",
|
||||
"sha2",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.15.0"
|
||||
@@ -750,6 +860,25 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[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",
|
||||
"ff",
|
||||
"generic-array",
|
||||
"group",
|
||||
"pkcs8",
|
||||
"rand_core 0.6.4",
|
||||
"sec1",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_filter"
|
||||
version = "0.1.4"
|
||||
@@ -833,6 +962,22 @@ version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[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 = "fiat-crypto"
|
||||
version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.5"
|
||||
@@ -982,6 +1127,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1025,6 +1171,17 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[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 = "hashbrown"
|
||||
version = "0.12.3"
|
||||
@@ -1179,12 +1336,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hyper-util"
|
||||
version = "0.1.18"
|
||||
version = "0.1.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52e9a2a24dc5c6821e71a7030e1e14b7b632acac55c40e9d2e082c621261bb56"
|
||||
checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"http",
|
||||
"http-body",
|
||||
"hyper",
|
||||
@@ -1355,6 +1511,15 @@ dependencies = [
|
||||
"syn 2.0.110",
|
||||
]
|
||||
|
||||
[[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"
|
||||
@@ -1659,18 +1824,22 @@ dependencies = [
|
||||
name = "ox_speak_server"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"argon2",
|
||||
"axum",
|
||||
"base64",
|
||||
"chrono",
|
||||
"env_logger",
|
||||
"futures-util",
|
||||
"log",
|
||||
"migration",
|
||||
"parking_lot",
|
||||
"rand 0.9.2",
|
||||
"sea-orm",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
"socket2",
|
||||
"ssh-key",
|
||||
"tokio",
|
||||
"toml",
|
||||
"tower-http",
|
||||
@@ -1678,6 +1847,44 @@ dependencies = [
|
||||
"validator",
|
||||
]
|
||||
|
||||
[[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",
|
||||
]
|
||||
|
||||
[[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",
|
||||
]
|
||||
|
||||
[[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",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking"
|
||||
version = "2.2.1"
|
||||
@@ -1707,6 +1914,17 @@ dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "password-hash"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"rand_core 0.6.4",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pem-rfc7468"
|
||||
version = "0.7.0"
|
||||
@@ -1844,6 +2062,15 @@ dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[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-macro-crate"
|
||||
version = "3.4.0"
|
||||
@@ -2044,6 +2271,16 @@ dependencies = [
|
||||
"bytecheck",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rfc6979"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2"
|
||||
dependencies = [
|
||||
"hmac",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rkyv"
|
||||
version = "0.7.45"
|
||||
@@ -2087,6 +2324,7 @@ dependencies = [
|
||||
"pkcs1",
|
||||
"pkcs8",
|
||||
"rand_core 0.6.4",
|
||||
"sha2",
|
||||
"signature",
|
||||
"spki",
|
||||
"subtle",
|
||||
@@ -2109,6 +2347,15 @@ dependencies = [
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.1.2"
|
||||
@@ -2155,9 +2402,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sea-orm"
|
||||
version = "2.0.0-rc.19"
|
||||
version = "2.0.0-rc.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee6dda57d64724c4c3e2b39ce17ca5f4084561656a3518b65b26edc5b36e4607"
|
||||
checksum = "f4bb965a287ae073c738851c5d38037ac6da66c9841ac1de7c13c8d08862180a"
|
||||
dependencies = [
|
||||
"async-stream",
|
||||
"async-trait",
|
||||
@@ -2204,11 +2451,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sea-orm-macros"
|
||||
version = "2.0.0-rc.19"
|
||||
version = "2.0.0-rc.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e7674a565e093a4bfffbfd6d7fd79a5dc8d75463d442ffb44d0fc3a3dcce5a6"
|
||||
checksum = "b3e208f041129ad7962b6951f0b392e9ff97a8337bd8c7022c61e7b02ab29fe0"
|
||||
dependencies = [
|
||||
"heck 0.5.0",
|
||||
"itertools",
|
||||
"pluralizer",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2235,11 +2483,10 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sea-query"
|
||||
version = "1.0.0-rc.20"
|
||||
version = "1.0.0-rc.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89ebab2b9d558deec08e43887a63ed4d96d56b32cb9d98578bd1749e2c8c7e24"
|
||||
checksum = "c6a067a2f6f13250f615f0bedb5bc3a6c872fec70776d0b43b43caeaa699e232"
|
||||
dependencies = [
|
||||
"bigdecimal",
|
||||
"chrono",
|
||||
"inherent",
|
||||
"ordered-float",
|
||||
@@ -2252,9 +2499,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sea-query-derive"
|
||||
version = "1.0.0-rc.11"
|
||||
version = "1.0.0-rc.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "365d236217f5daa4f40d3c9998ff3921351b53472da50308e384388162353b3a"
|
||||
checksum = "8d88ad44b6ad9788c8b9476b6b91f94c7461d1e19d39cd8ea37838b1e6ff5aa8"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"heck 0.4.1",
|
||||
@@ -2266,18 +2513,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sea-query-sqlx"
|
||||
version = "0.8.0-rc.9"
|
||||
version = "0.8.0-rc.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68873fa1776b4c25a26e7679f8ee22332978c721168ec1b0b32b6583d5a9381d"
|
||||
checksum = "e4377164b09a11bb692dec6966eb0e6908d63d768defef0be689b39e02cf8544"
|
||||
dependencies = [
|
||||
"bigdecimal",
|
||||
"chrono",
|
||||
"rust_decimal",
|
||||
"sea-query",
|
||||
"serde_json",
|
||||
"sqlx",
|
||||
"time",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2311,6 +2552,26 @@ version = "4.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
|
||||
|
||||
[[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"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
@@ -2343,15 +2604,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.145"
|
||||
version = "1.0.149"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
|
||||
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
"serde_core",
|
||||
"zmij",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2523,7 +2784,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bigdecimal",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"crc",
|
||||
@@ -2601,7 +2861,6 @@ checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526"
|
||||
dependencies = [
|
||||
"atoi",
|
||||
"base64",
|
||||
"bigdecimal",
|
||||
"bitflags",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
@@ -2648,7 +2907,6 @@ checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46"
|
||||
dependencies = [
|
||||
"atoi",
|
||||
"base64",
|
||||
"bigdecimal",
|
||||
"bitflags",
|
||||
"byteorder",
|
||||
"chrono",
|
||||
@@ -2666,7 +2924,6 @@ dependencies = [
|
||||
"log",
|
||||
"md-5",
|
||||
"memchr",
|
||||
"num-bigint",
|
||||
"once_cell",
|
||||
"rand 0.8.5",
|
||||
"rust_decimal",
|
||||
@@ -2710,6 +2967,49 @@ dependencies = [
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[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",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ssh-key"
|
||||
version = "0.6.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b86f5297f0f04d08cabaa0f6bff7cb6aec4d9c3b49d87990d63da9d9156a8c3"
|
||||
dependencies = [
|
||||
"ed25519-dalek",
|
||||
"num-bigint-dig",
|
||||
"p256",
|
||||
"p384",
|
||||
"p521",
|
||||
"rand_core 0.6.4",
|
||||
"rsa",
|
||||
"sec1",
|
||||
"sha2",
|
||||
"signature",
|
||||
"ssh-cipher",
|
||||
"ssh-encoding",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.1"
|
||||
@@ -2883,9 +3183,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.48.0"
|
||||
version = "1.49.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408"
|
||||
checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"libc",
|
||||
@@ -3163,14 +3463,14 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.18.1"
|
||||
version = "1.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2"
|
||||
checksum = "ee48d38b119b0cd71fe4141b30f5ba9c7c5d9f4e7a3a8b4a674e4b6ef789976f"
|
||||
dependencies = [
|
||||
"getrandom 0.3.4",
|
||||
"js-sys",
|
||||
"rand 0.9.2",
|
||||
"serde",
|
||||
"serde_core",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
@@ -3667,3 +3967,9 @@ dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.110",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "1.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ff05f8caa9038894637571ae6b9e29466c1f4f829d26c9b28f869a29cbe3445"
|
||||
|
||||
21
Cargo.toml
21
Cargo.toml
@@ -2,6 +2,7 @@
|
||||
name = "ox_speak_server"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
build = "build.rs"
|
||||
|
||||
[lib]
|
||||
# The `_lib` suffix may seem redundant but it is necessary
|
||||
@@ -34,13 +35,13 @@ opt-level = 3 # Optimisation maximale pour la vitesse
|
||||
|
||||
[dependencies]
|
||||
# Async
|
||||
tokio = {version = "1.48", features = ["full"]}
|
||||
tokio = { version = "1.49", features = ["full"] }
|
||||
|
||||
# HTTP
|
||||
#actix-web = "4.12"
|
||||
#poem = "3.1"
|
||||
#poem-openapi = { version="5.1", features = ["swagger-ui", "url", "chrono"]}
|
||||
axum = { version = "0.8", features = ["macros", "ws"] }
|
||||
axum = { version = "0.8.8", features = ["macros", "ws"] }
|
||||
#utoipa = "5.4"
|
||||
#utoipa-swagger-ui = { version = "9.0", features = ["axum"] }
|
||||
#tower = "0.5"
|
||||
@@ -50,20 +51,24 @@ tower-http = { version = "0.6", features = ["trace", "cors", "timeout"] }
|
||||
socket2 = "0.6"
|
||||
|
||||
# db
|
||||
sea-orm = { version = "2.0.0-rc", features = ["sqlx-sqlite", "sqlx-postgres", "sqlx-mysql", "runtime-tokio", "with-chrono", "with-uuid", "with-json", "schema-sync"] }
|
||||
sea-orm = { version = "2.0.0-rc.30", features = ["sqlx-sqlite", "sqlx-postgres", "sqlx-mysql", "runtime-tokio", "with-chrono", "with-uuid", "with-json", "schema-sync"] }
|
||||
migration = { path = "migration" }
|
||||
|
||||
# logs
|
||||
log = "0.4.28"
|
||||
log = "0.4"
|
||||
env_logger = "0.11.8"
|
||||
|
||||
# utils
|
||||
chrono = "0.4"
|
||||
chrono = "0.4.43"
|
||||
parking_lot = "0.12"
|
||||
serde = { version = "1.0", features = ["default", "derive"] }
|
||||
serde_json = { version = "1.0.145", features = ["default"]}
|
||||
serde_json = { version = "1.0.149", features = ["default"] }
|
||||
serde_repr = "0.1"
|
||||
toml = "0.9"
|
||||
toml = "0.9.8"
|
||||
validator = { version = "0.20", features = ["derive"] }
|
||||
uuid = {version = "1", features = ["v4", "v7", "fast-rng", "serde"]}
|
||||
uuid = { version = "1.20", features = ["v4", "v7", "fast-rng", "serde"] }
|
||||
futures-util = "0.3"
|
||||
rand = "0.9"
|
||||
ssh-key = { version = "0.6", features = ["default", "crypto"] }
|
||||
base64 = "0.22"
|
||||
argon2 = "0.5.3"
|
||||
3
README.md
Normal file
3
README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## TODO
|
||||
|
||||
Terminer le système d'authentification (login, changement de mot de passe...)
|
||||
3
build.rs
Normal file
3
build.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
fn main() {
|
||||
let profile = std::env::var("PROFILE").unwrap_or_default();
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -85,4 +85,8 @@ impl App {
|
||||
|
||||
println!("Nettoyage et fermeture de l'application.");
|
||||
}
|
||||
|
||||
async fn shutdown(&self) {
|
||||
|
||||
}
|
||||
}
|
||||
35
src/interfaces/http/dto/category.rs
Normal file
35
src/interfaces/http/dto/category.rs
Normal file
@@ -0,0 +1,35 @@
|
||||
use crate::models::category;
|
||||
use sea_orm::Set;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct CategoryResponse {
|
||||
pub id: Uuid,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
impl From<category::Model> for CategoryResponse {
|
||||
fn from(model: category::Model) -> Self {
|
||||
Self {
|
||||
id: model.id,
|
||||
name: model.name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct CreateCategoryRequest {
|
||||
pub server_id: Uuid,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
impl From<CreateCategoryRequest> for category::ActiveModel {
|
||||
fn from(request: CreateCategoryRequest) -> Self {
|
||||
Self {
|
||||
server_id: Set(request.server_id),
|
||||
name: Set(request.name),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
50
src/interfaces/http/dto/channel.rs
Normal file
50
src/interfaces/http/dto/channel.rs
Normal file
@@ -0,0 +1,50 @@
|
||||
use crate::models::channel;
|
||||
use crate::models::channel::ChannelType;
|
||||
use sea_orm::Set;
|
||||
use serde::Serialize;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct ChannelResponse {
|
||||
pub id: Uuid,
|
||||
pub server_id: Option<Uuid>,
|
||||
pub category_id: Option<Uuid>,
|
||||
pub position: i32,
|
||||
pub channel_type: ChannelType,
|
||||
pub name: Option<String>,
|
||||
}
|
||||
|
||||
impl From<channel::Model> for ChannelResponse {
|
||||
fn from(model: channel::Model) -> Self {
|
||||
Self {
|
||||
id: model.id,
|
||||
server_id: model.server_id,
|
||||
category_id: model.category_id,
|
||||
position: model.position,
|
||||
channel_type: model.channel_type,
|
||||
name: model.name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct CreateChannelRequest {
|
||||
pub server_id: Option<Uuid>,
|
||||
pub category_id: Option<Uuid>,
|
||||
pub position: i32,
|
||||
pub channel_type: ChannelType,
|
||||
pub name: Option<String>,
|
||||
}
|
||||
|
||||
impl From<CreateChannelRequest> for channel::ActiveModel {
|
||||
fn from(request: CreateChannelRequest) -> Self {
|
||||
Self {
|
||||
server_id: Set(request.server_id),
|
||||
category_id: Set(request.category_id),
|
||||
position: Set(request.position),
|
||||
channel_type: Set(request.channel_type),
|
||||
name: Set(request.name),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
40
src/interfaces/http/dto/message.rs
Normal file
40
src/interfaces/http/dto/message.rs
Normal file
@@ -0,0 +1,40 @@
|
||||
use crate::models::message;
|
||||
use sea_orm::Set;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct MessageResponse {
|
||||
pub id: Uuid,
|
||||
pub channel_id: Uuid,
|
||||
pub author_id: Uuid,
|
||||
pub content: String,
|
||||
}
|
||||
|
||||
impl From<message::Model> for MessageResponse {
|
||||
fn from(model: message::Model) -> Self {
|
||||
Self {
|
||||
id: model.id,
|
||||
channel_id: model.channel_id,
|
||||
author_id: model.user_id,
|
||||
content: model.content,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct CreateMessageRequest {
|
||||
pub channel_id: Uuid,
|
||||
pub content: String,
|
||||
}
|
||||
|
||||
impl From<CreateMessageRequest> for message::ActiveModel {
|
||||
fn from(request: CreateMessageRequest) -> Self {
|
||||
Self {
|
||||
channel_id: Set(request.channel_id),
|
||||
user_id: Set(Uuid::new_v4()),
|
||||
content: Set(request.content),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
6
src/interfaces/http/dto/mod.rs
Normal file
6
src/interfaces/http/dto/mod.rs
Normal file
@@ -0,0 +1,6 @@
|
||||
pub mod auth;
|
||||
pub mod category;
|
||||
pub mod channel;
|
||||
pub mod message;
|
||||
pub mod server;
|
||||
pub mod user;
|
||||
52
src/interfaces/http/dto/server.rs
Normal file
52
src/interfaces/http/dto/server.rs
Normal file
@@ -0,0 +1,52 @@
|
||||
// On importe le modèle pour pouvoir mapper
|
||||
use crate::models::server;
|
||||
use sea_orm::Set;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct ServerResponse {
|
||||
pub id: Uuid,
|
||||
pub name: String,
|
||||
pub created_at: String,
|
||||
pub updated_at: String,
|
||||
}
|
||||
|
||||
impl From<server::Model> for ServerResponse {
|
||||
fn from(model: server::Model) -> Self {
|
||||
Self {
|
||||
id: model.id,
|
||||
name: model.name,
|
||||
created_at: model.created_at.to_rfc3339(),
|
||||
updated_at: model.updated_at.to_rfc3339(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct CreateServerRequest {
|
||||
pub name: String,
|
||||
pub password: Option<String>,
|
||||
}
|
||||
|
||||
impl From<CreateServerRequest> for server::ActiveModel {
|
||||
fn from(request: CreateServerRequest) -> Self {
|
||||
Self {
|
||||
name: Set(request.name),
|
||||
password: Set(request.password),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum TreeItemType {
|
||||
// todo : faire le CategoryResponse et ChannelResponse
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct ServerTreeResponse {
|
||||
items: Vec<TreeItemType>,
|
||||
}
|
||||
33
src/interfaces/http/dto/user.rs
Normal file
33
src/interfaces/http/dto/user.rs
Normal file
@@ -0,0 +1,33 @@
|
||||
use crate::models::user;
|
||||
use sea_orm::Set;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct UserResponse {
|
||||
pub id: Uuid,
|
||||
pub username: String,
|
||||
}
|
||||
|
||||
impl From<user::Model> for UserResponse {
|
||||
fn from(model: user::Model) -> Self {
|
||||
Self {
|
||||
id: model.id,
|
||||
username: model.username,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct CreateUserRequest {
|
||||
pub username: String,
|
||||
}
|
||||
|
||||
impl From<CreateUserRequest> for user::ActiveModel {
|
||||
fn from(request: CreateUserRequest) -> Self {
|
||||
Self {
|
||||
username: Set(request.username),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
1
src/interfaces/http/mod.rs
Normal file
1
src/interfaces/http/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod dto;
|
||||
1
src/interfaces/mod.rs
Normal file
1
src/interfaces/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod http;
|
||||
12
src/lib.rs
12
src/lib.rs
@@ -1,11 +1,13 @@
|
||||
pub mod config;
|
||||
pub mod app;
|
||||
pub mod config;
|
||||
pub mod network;
|
||||
pub mod utils;
|
||||
|
||||
pub mod database;
|
||||
pub mod models;
|
||||
pub mod serializers;
|
||||
pub mod repositories;
|
||||
pub mod event_bus;
|
||||
pub mod hub;
|
||||
pub mod hub;
|
||||
pub mod models;
|
||||
pub mod repositories;
|
||||
pub mod serializers;
|
||||
|
||||
pub mod interfaces;
|
||||
|
||||
@@ -13,7 +13,7 @@ pub struct Model {
|
||||
pub filename: String,
|
||||
pub file_size: i32,
|
||||
pub mime_type: String,
|
||||
pub created_at: DateTime,
|
||||
pub created_at: DateTimeUtc,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
|
||||
@@ -12,8 +12,8 @@ pub struct Model {
|
||||
pub server_id: Uuid,
|
||||
pub name: String,
|
||||
pub position: i32,
|
||||
pub created_at: DateTime,
|
||||
pub updated_at: DateTime,
|
||||
pub created_at: DateTimeUtc,
|
||||
pub updated_at: DateTimeUtc,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
@@ -50,4 +50,4 @@ impl ActiveModelBehavior for ActiveModel {
|
||||
..ActiveModelTrait::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,8 +27,8 @@ pub struct Model {
|
||||
pub position: i32,
|
||||
pub channel_type: ChannelType,
|
||||
pub name: Option<String>,
|
||||
pub created_at: DateTime,
|
||||
pub updated_at: DateTime,
|
||||
pub created_at: DateTimeUtc,
|
||||
pub updated_at: DateTimeUtc,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
@@ -87,4 +87,4 @@ impl ActiveModelBehavior for ActiveModel {
|
||||
..ActiveModelTrait::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ pub struct Model {
|
||||
pub channel_id: Uuid,
|
||||
pub user_id: Uuid,
|
||||
pub role: String,
|
||||
pub joined_at: DateTime,
|
||||
pub joined_at: DateTimeUtc,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
@@ -55,4 +55,4 @@ impl ActiveModelBehavior for ActiveModel {
|
||||
..ActiveModelTrait::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,8 +13,8 @@ pub struct Model {
|
||||
pub user_id: Uuid,
|
||||
#[sea_orm(column_type = "Text")]
|
||||
pub content: String,
|
||||
pub created_at: DateTime,
|
||||
pub edited_at: Option<DateTime>,
|
||||
pub created_at: DateTimeUtc,
|
||||
pub updated_at: Option<DateTimeUtc>,
|
||||
pub reply_to_id: Option<Uuid>,
|
||||
}
|
||||
|
||||
@@ -74,4 +74,4 @@ impl ActiveModelBehavior for ActiveModel {
|
||||
..ActiveModelTrait::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@ pub struct Model {
|
||||
pub id: Uuid,
|
||||
pub name: String,
|
||||
pub password: Option<String>,
|
||||
pub created_at: DateTime,
|
||||
pub updated_at: DateTime,
|
||||
pub created_at: DateTimeUtc,
|
||||
pub updated_at: DateTimeUtc,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
@@ -51,4 +51,4 @@ impl ActiveModelBehavior for ActiveModel {
|
||||
..ActiveModelTrait::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@ pub struct Model {
|
||||
pub server_id: Uuid,
|
||||
pub user_id: Uuid,
|
||||
pub username: Option<String>,
|
||||
pub joined_at: DateTime,
|
||||
pub updated_at: DateTime,
|
||||
pub joined_at: DateTimeUtc,
|
||||
pub updated_at: DateTimeUtc,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
@@ -56,4 +56,4 @@ impl ActiveModelBehavior for ActiveModel {
|
||||
..ActiveModelTrait::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,10 +10,11 @@ pub struct Model {
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: Uuid,
|
||||
pub username: String,
|
||||
pub password: String,
|
||||
#[sea_orm(column_type = "Text", unique)]
|
||||
pub pub_key: String,
|
||||
pub created_at: DateTime,
|
||||
pub updated_at: DateTime,
|
||||
pub created_at: DateTimeUtc,
|
||||
pub updated_at: DateTimeUtc,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
@@ -52,4 +53,4 @@ impl ActiveModelBehavior for ActiveModel {
|
||||
..ActiveModelTrait::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::network::http::{web, AppRouter};
|
||||
pub fn setup_route(app_state: AppState) -> Router {
|
||||
Router::new()
|
||||
.merge(web::setup_route())
|
||||
.layer(CorsLayer::permissive())
|
||||
.layer(middleware::from_fn_with_state(app_state.clone(), context_middleware))
|
||||
.with_state(app_state)
|
||||
.layer(CorsLayer::permissive())
|
||||
}
|
||||
105
src/network/http/web/api/auth.rs
Normal file
105
src/network/http/web/api/auth.rs
Normal file
@@ -0,0 +1,105 @@
|
||||
use crate::app::AppState;
|
||||
use crate::network::http::{AppRouter, HTTPError};
|
||||
use crate::utils::toolbox::ssh_generate_challenge;
|
||||
use axum::extract::State;
|
||||
use axum::Json;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ssh_key::{Algorithm as SshAlgorithm, PublicKey, Signature};
|
||||
|
||||
fn setup_route() -> AppRouter {
|
||||
AppRouter::new()
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct SshChallengeRequest {
|
||||
username: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct SshChallengeResponse {
|
||||
challenge: String,
|
||||
}
|
||||
|
||||
pub async fn ssh_challenge(
|
||||
State(state): State<AppState>,
|
||||
Json(payload): Json<SshChallengeRequest>,
|
||||
) -> Result<Json<SshChallengeResponse>, HTTPError> {
|
||||
log::info!(
|
||||
"POST /auth/ssh-challenge - Challenge request for user: {}",
|
||||
payload.username
|
||||
);
|
||||
|
||||
let user = state
|
||||
.repositories
|
||||
.user
|
||||
.get_by_username(payload.username.clone())
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
log::warn!(
|
||||
"POST /auth/ssh-challenge - User not found: {}",
|
||||
payload.username
|
||||
);
|
||||
HTTPError::NotFound
|
||||
})?;
|
||||
|
||||
let challenge = ssh_generate_challenge(32);
|
||||
log::info!(
|
||||
"POST /auth/ssh-challenge - Challenge generated for user: {}",
|
||||
payload.username
|
||||
);
|
||||
|
||||
// todo : stocker le challenge dans AppState
|
||||
// bien penser à ajouter un délai d'expiration
|
||||
// songer à mettre une session id ?
|
||||
// Peut être que l'utilisateur se connectera depuis différent device
|
||||
// state.store_challenge(&payload.username, challenge.clone())
|
||||
|
||||
Ok(Json(SshChallengeResponse { challenge }))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
enum SignatureAlgorithm {
|
||||
#[serde(rename = "rsa")]
|
||||
Rsa,
|
||||
#[serde(rename = "ed25519")]
|
||||
Ed25519,
|
||||
#[serde(rename = "ecdsa")]
|
||||
Ecdsa,
|
||||
}
|
||||
|
||||
impl SignatureAlgorithm {
|
||||
fn to_ssh_algorithm(&self) -> SshAlgorithm {
|
||||
match self {
|
||||
SignatureAlgorithm::Rsa => SshAlgorithm::Rsa { hash: None },
|
||||
SignatureAlgorithm::Ed25519 => SshAlgorithm::Ed25519,
|
||||
SignatureAlgorithm::Ecdsa => SshAlgorithm::Ecdsa {
|
||||
curve: ssh_key::EcdsaCurve::NistP256,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct SshVerifyRequest {
|
||||
username: String,
|
||||
signature: String,
|
||||
algorithm: SignatureAlgorithm,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct SshVerifyResponse {
|
||||
// todo : remplir avec la réponse jwt - à établir après la construction jwt
|
||||
}
|
||||
|
||||
pub async fn ssh_verify() {}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct LoginRequest {
|
||||
username: String,
|
||||
password: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct SshChallengeResponse {
|
||||
challenge: String,
|
||||
}
|
||||
@@ -30,6 +30,8 @@ pub async fn category_list(
|
||||
Extension(_ctx): Extension<RequestContext>,
|
||||
Query(query): Query<CategoryQuery>
|
||||
) -> Result<Json<Vec<CategorySerializer>>, HTTPError> {
|
||||
log::info!("GET /categories/ - Query: server_id={:?}", query.server_id);
|
||||
|
||||
let categories = if let Some(server_id) = query.server_id {
|
||||
app_state.repositories.category
|
||||
.get_by_server_id(server_id)
|
||||
@@ -40,7 +42,7 @@ pub async fn category_list(
|
||||
.await?
|
||||
};
|
||||
|
||||
|
||||
log::info!("GET /categories/ - Found {} categories", categories.len());
|
||||
Ok(Json(categories.into_iter().map(CategorySerializer::from).collect()))
|
||||
}
|
||||
|
||||
@@ -48,11 +50,17 @@ pub async fn category_detail(
|
||||
State(app_state): State<AppState>,
|
||||
Path(id): Path<Uuid>
|
||||
) -> Result<Json<CategorySerializer>, HTTPError> {
|
||||
let category = category::Entity::find_by_id(id)
|
||||
.one(app_state.db.get_connection())
|
||||
.await?
|
||||
.ok_or(HTTPError::NotFound)?;
|
||||
log::info!("GET /categories/{id}/ - Fetching category: {}", id);
|
||||
|
||||
let category = app_state.repositories.category
|
||||
.get_by_id(id)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
log::warn!("GET /categories/{id}/ - Category not found: {}", id);
|
||||
HTTPError::NotFound
|
||||
})?;
|
||||
|
||||
log::info!("GET /categories/{id}/ - Category found: {}", id);
|
||||
Ok(Json(CategorySerializer::from(category)))
|
||||
}
|
||||
|
||||
@@ -60,9 +68,12 @@ pub async fn category_create(
|
||||
State(app_state): State<AppState>,
|
||||
Json(serializer): Json<CategorySerializer>
|
||||
) -> Result<Json<CategorySerializer>, HTTPError> {
|
||||
log::info!("POST /categories/ - Creating category: {:?}", serializer.name);
|
||||
|
||||
let active = serializer.into_active_model();
|
||||
let category: category::Model = active.insert(app_state.db.get_connection()).await?;
|
||||
|
||||
log::info!("POST /categories/ - Category created with id: {}", category.id);
|
||||
Ok(Json(CategorySerializer::from(category)))
|
||||
}
|
||||
|
||||
@@ -71,10 +82,15 @@ pub async fn category_update(
|
||||
Path(id): Path<Uuid>,
|
||||
Json(serializer): Json<CategorySerializer>,
|
||||
) -> Result<Json<CategorySerializer>, HTTPError> {
|
||||
log::info!("PUT /categories/{id}/ - Updating category: {}", id);
|
||||
|
||||
let category = category::Entity::find_by_id(id)
|
||||
.one(app_state.db.get_connection())
|
||||
.await?
|
||||
.ok_or(HTTPError::NotFound)?;
|
||||
.ok_or_else(|| {
|
||||
log::warn!("PUT /categories/{id}/ - Category not found: {}", id);
|
||||
HTTPError::NotFound
|
||||
})?;
|
||||
|
||||
let active = category.into_active_model();
|
||||
|
||||
@@ -82,6 +98,7 @@ pub async fn category_update(
|
||||
.update(app_state.db.get_connection())
|
||||
.await?;
|
||||
|
||||
log::info!("PUT /categories/{id}/ - Category updated: {}", id);
|
||||
Ok(Json(CategorySerializer::from(category)))
|
||||
}
|
||||
|
||||
@@ -89,13 +106,17 @@ pub async fn category_delete(
|
||||
State(app_state): State<AppState>,
|
||||
Path(id): Path<Uuid>
|
||||
) -> Result<StatusCode, HTTPError> {
|
||||
log::info!("DELETE /categories/{id}/ - Deleting category: {}", id);
|
||||
|
||||
let result = category::Entity::delete_by_id(id)
|
||||
.exec(app_state.db.get_connection())
|
||||
.await?;
|
||||
|
||||
if result.rows_affected == 0 {
|
||||
log::warn!("DELETE /categories/{id}/ - Category not found: {}", id);
|
||||
Err(HTTPError::NotFound)
|
||||
} else {
|
||||
log::info!("DELETE /categories/{id}/ - Category deleted: {}", id);
|
||||
Ok(StatusCode::NO_CONTENT)
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@ mod category;
|
||||
mod channel;
|
||||
mod message;
|
||||
mod server;
|
||||
mod user;
|
||||
mod auth;
|
||||
|
||||
pub fn setup_route() -> AppRouter {
|
||||
|
||||
@@ -12,4 +14,5 @@ pub fn setup_route() -> AppRouter {
|
||||
.nest("/channel", channel::setup_route())
|
||||
.nest("/message", message::setup_route())
|
||||
.nest("/server", server::setup_route())
|
||||
.nest("/user", user::setup_route())
|
||||
}
|
||||
@@ -1,75 +1,84 @@
|
||||
use axum::Json;
|
||||
use axum::extract::{Path, State};
|
||||
use axum::http::StatusCode;
|
||||
use axum::routing::{delete, get, post, put};
|
||||
use sea_orm::{IntoActiveModel};
|
||||
use uuid::Uuid;
|
||||
use crate::app::AppState;
|
||||
use crate::models::server;
|
||||
use crate::interfaces::http::dto::server::ServerResponse;
|
||||
use crate::network::http::{AppRouter, HTTPError};
|
||||
use crate::serializers::{ServerSerializer, ServerTreeSerializer};
|
||||
use axum::extract::{Path, State};
|
||||
use axum::http::StatusCode;
|
||||
use axum::routing::get;
|
||||
use axum::Json;
|
||||
use sea_orm::IntoActiveModel;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub fn setup_route() -> AppRouter {
|
||||
AppRouter::new()
|
||||
.route("/servers/", get(server_list))
|
||||
.route("/servers/{id}/", get(server_detail))
|
||||
.route("/servers/", post(server_create))
|
||||
.route("/servers/{id}/", put(server_update))
|
||||
.route("/servers/{id}/", delete(server_delete))
|
||||
.route("/servers/", get(server_list).post(server_create))
|
||||
.route(
|
||||
"/servers/{id}/",
|
||||
get(server_detail).put(server_update).delete(server_delete),
|
||||
)
|
||||
.route("/servers/{id}/password/", get(server_password))
|
||||
.route("/servers/{id}/tree/", get(tree))
|
||||
}
|
||||
|
||||
pub async fn server_list(
|
||||
State(state): State<AppState>
|
||||
) -> Result<Json<Vec<ServerSerializer>>, HTTPError> {
|
||||
State(state): State<AppState>,
|
||||
) -> Result<Json<Vec<ServerResponse>>, HTTPError> {
|
||||
let servers = state.repositories.server.get_all().await?;
|
||||
|
||||
Ok(Json(servers.into_iter().map(ServerSerializer::from).collect()))
|
||||
Ok(Json(
|
||||
servers.into_iter().map(ServerResponse::from).collect(),
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn server_detail(
|
||||
State(state): State<AppState>,
|
||||
Path(id): Path<Uuid>
|
||||
) -> Result<Json<ServerSerializer>, HTTPError> {
|
||||
let server = state.repositories.server.get_by_id(id)
|
||||
Path(id): Path<Uuid>,
|
||||
) -> Result<Json<ServerResponse>, HTTPError> {
|
||||
let server = state
|
||||
.repositories
|
||||
.server
|
||||
.get_by_id(id)
|
||||
.await?
|
||||
.ok_or(HTTPError::NotFound)?;
|
||||
|
||||
Ok(Json(ServerSerializer::from(server)))
|
||||
Ok(Json(ServerResponse::from(server)))
|
||||
}
|
||||
|
||||
pub async fn server_create(
|
||||
State(state): State<AppState>,
|
||||
Json(serializer): Json<ServerSerializer>
|
||||
) -> Result<Json<ServerSerializer>, HTTPError> {
|
||||
Json(serializer): Json<ServerSerializer>,
|
||||
) -> Result<Json<ServerResponse>, HTTPError> {
|
||||
let active = serializer.into_active_model();
|
||||
let server = state.repositories.server.create(active).await?;
|
||||
|
||||
Ok(Json(ServerSerializer::from(server)))
|
||||
Ok(Json(ServerResponse::from(server)))
|
||||
}
|
||||
|
||||
pub async fn server_update(
|
||||
State(state): State<AppState>,
|
||||
Path(id): Path<Uuid>,
|
||||
Json(serializer): Json<ServerSerializer>,
|
||||
) -> Result<Json<ServerSerializer>, HTTPError> {
|
||||
let am_server = state.repositories.server
|
||||
) -> Result<Json<ServerResponse>, HTTPError> {
|
||||
let am_server = state
|
||||
.repositories
|
||||
.server
|
||||
.get_by_id(id)
|
||||
.await?
|
||||
.ok_or(HTTPError::NotFound)?
|
||||
.into_active_model();
|
||||
|
||||
let server = state.repositories.server
|
||||
let server = state
|
||||
.repositories
|
||||
.server
|
||||
.update(serializer.apply_to_active_model(am_server))
|
||||
.await?;
|
||||
|
||||
Ok(Json(ServerSerializer::from(server)))
|
||||
Ok(Json(ServerResponse::from(server)))
|
||||
}
|
||||
|
||||
pub async fn server_delete(
|
||||
State(state): State<AppState>,
|
||||
Path(id): Path<Uuid>
|
||||
Path(id): Path<Uuid>,
|
||||
) -> Result<StatusCode, HTTPError> {
|
||||
if state.repositories.server.delete(id).await? {
|
||||
Ok(StatusCode::NO_CONTENT)
|
||||
@@ -80,9 +89,12 @@ pub async fn server_delete(
|
||||
|
||||
pub async fn server_password(
|
||||
State(state): State<AppState>,
|
||||
Path(id): Path<Uuid>
|
||||
Path(id): Path<Uuid>,
|
||||
) -> Result<Json<Option<String>>, HTTPError> {
|
||||
let server = state.repositories.server.get_by_id(id)
|
||||
let server = state
|
||||
.repositories
|
||||
.server
|
||||
.get_by_id(id)
|
||||
.await?
|
||||
.ok_or(HTTPError::NotFound)?;
|
||||
|
||||
@@ -91,9 +103,9 @@ pub async fn server_password(
|
||||
|
||||
pub async fn tree(
|
||||
State(state): State<AppState>,
|
||||
Path(id): Path<Uuid>
|
||||
Path(id): Path<Uuid>,
|
||||
) -> Result<Json<ServerTreeSerializer>, HTTPError> {
|
||||
let layout = state.repositories.server.get_tree(id).await?;
|
||||
|
||||
Ok(Json(ServerTreeSerializer::from(layout)))
|
||||
}
|
||||
}
|
||||
|
||||
42
src/network/http/web/api/user.rs
Normal file
42
src/network/http/web/api/user.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
use axum::{Extension, Json};
|
||||
use axum::extract::{Path, State};
|
||||
use axum::routing::get;
|
||||
use uuid::Uuid;
|
||||
use crate::app::AppState;
|
||||
use crate::network::http::{AppRouter, HTTPError};
|
||||
use crate::serializers::UserSerializer;
|
||||
|
||||
pub fn setup_route() -> AppRouter {
|
||||
AppRouter::new()
|
||||
.route("/users/", get(user_list))
|
||||
.route("/users/{id}/", get(user_detail))
|
||||
}
|
||||
|
||||
pub async fn user_list(
|
||||
State(app_state): State<AppState>,
|
||||
Extension(_ctx): Extension<crate::network::http::RequestContext>
|
||||
) -> Result<Json<Vec<UserSerializer>>, HTTPError> {
|
||||
log::info!("GET /users/ - Fetching user list");
|
||||
|
||||
let users = app_state.repositories.user.get_all().await?;
|
||||
log::info!("GET /users/ - Found {} users", users.len());
|
||||
|
||||
Ok(Json(users.into_iter().map(UserSerializer::from).collect()))
|
||||
}
|
||||
|
||||
pub async fn user_detail(
|
||||
State(app_state): State<AppState>,
|
||||
Path(id): Path<Uuid>
|
||||
) -> Result<Json<UserSerializer>, HTTPError> {
|
||||
log::info!("GET /users/{id}/ - Fetching user with id: {}", id);
|
||||
|
||||
let user = app_state.repositories.user
|
||||
.get_by_id(id).await?
|
||||
.ok_or_else(|| {
|
||||
log::warn!("GET /users/{id}/ - User not found: {}", id);
|
||||
HTTPError::NotFound
|
||||
})?;
|
||||
|
||||
log::info!("GET /users/{id}/ - User found: {}", id);
|
||||
Ok(Json(UserSerializer::from(user)))
|
||||
}
|
||||
@@ -10,7 +10,9 @@ use crate::utils::toolbox::number_of_cpus;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct UDPServer {
|
||||
// todo : passer sur du arcswap, rwlock rajoute trop de contention.
|
||||
table_router: Arc<RwLock<HashMap<String, SocketAddr>>>,
|
||||
|
||||
bind_addr: SocketAddr
|
||||
}
|
||||
|
||||
|
||||
@@ -1,33 +1,83 @@
|
||||
use std::sync::Arc;
|
||||
use sea_orm::{DbErr, EntityTrait, ActiveModelTrait};
|
||||
use crate::models::user;
|
||||
use crate::repositories::RepositoryContext;
|
||||
use crate::utils::password;
|
||||
use sea_orm::{
|
||||
ActiveModelTrait, ColumnTrait, DbErr, EntityTrait, IntoActiveModel, QueryFilter, Set,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct UserRepository {
|
||||
pub context: Arc<RepositoryContext>
|
||||
pub context: Arc<RepositoryContext>,
|
||||
}
|
||||
|
||||
impl UserRepository {
|
||||
pub async fn get_all(&self) -> Result<Vec<user::Model>, DbErr> {
|
||||
user::Entity::find().all(&self.context.db).await
|
||||
}
|
||||
|
||||
pub async fn get_by_id(&self, id: uuid::Uuid) -> Result<Option<user::Model>, DbErr> {
|
||||
user::Entity::find_by_id(id).one(&self.context.db).await
|
||||
}
|
||||
|
||||
pub async fn get_by_username(&self, username: String) -> Result<Option<user::Model>, DbErr> {
|
||||
user::Entity::find()
|
||||
.filter(user::Column::Username.eq(username))
|
||||
.one(&self.context.db)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn check_password(
|
||||
&self,
|
||||
username: String,
|
||||
password: String,
|
||||
) -> Result<user::Model, DbErr> {
|
||||
let user = self.get_by_username(username).await?;
|
||||
if let Some(user) = user {
|
||||
let password_ok = password::verify_password(password.as_str(), user.password.as_str())
|
||||
.map_err(|_| DbErr::Custom("Password hashing failed".to_string()))?;
|
||||
|
||||
if password_ok {
|
||||
return Ok(user);
|
||||
}
|
||||
}
|
||||
Err(DbErr::Custom("Invalid username or password".to_string()))
|
||||
}
|
||||
|
||||
pub async fn update(&self, active: user::ActiveModel) -> Result<user::Model, DbErr> {
|
||||
let user = active.update(&self.context.db).await?;
|
||||
self.context.events.emit("user_updated", user.clone());
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
pub async fn create(&self, active: user::ActiveModel) -> Result<user::Model, DbErr> {
|
||||
pub async fn create(&self, mut active: user::ActiveModel) -> Result<user::Model, DbErr> {
|
||||
let user = active.insert(&self.context.db).await?;
|
||||
self.context.events.emit("user_created", user.clone());
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
pub async fn set_password(&self, id: uuid::Uuid, password: String) -> Result<(), DbErr> {
|
||||
let user = self
|
||||
.get_by_id(id)
|
||||
.await?
|
||||
.ok_or_else(|| DbErr::Custom("User not found".to_string()))?;
|
||||
|
||||
let mut active = user.into_active_model();
|
||||
let password = password::hash_password(&password)
|
||||
.map_err(|_| DbErr::Custom("Password hashing failed".to_string()))?;
|
||||
active.password = Set(password);
|
||||
|
||||
let user = self.update(active).await?;
|
||||
|
||||
self.context.events.emit("user_changed", user);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn delete(&self, id: uuid::Uuid) -> Result<(), DbErr> {
|
||||
user::Entity::delete_by_id(id).exec(&self.context.db).await?;
|
||||
user::Entity::delete_by_id(id)
|
||||
.exec(&self.context.db)
|
||||
.await?;
|
||||
self.context.events.emit("user_deleted", id);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,20 @@ mod server;
|
||||
mod category;
|
||||
mod channel;
|
||||
mod message;
|
||||
mod user;
|
||||
|
||||
trait BaseSerializer<M>
|
||||
where
|
||||
M: ActiveModelTrait,
|
||||
{
|
||||
fn into_active_model(self) -> M;
|
||||
|
||||
fn apply_to_active_model(self, active_model: M) -> M;
|
||||
}
|
||||
|
||||
use sea_orm::ActiveModelTrait;
|
||||
pub use server::*;
|
||||
pub use category::*;
|
||||
pub use channel::*;
|
||||
pub use message::*;
|
||||
pub use message::*;
|
||||
pub use user::*;
|
||||
44
src/serializers/user.rs
Normal file
44
src/serializers/user.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
use sea_orm::ActiveValue::Set;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
use crate::models::{category, user};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct UserSerializer {
|
||||
#[serde(skip_deserializing)]
|
||||
pub id: Option<Uuid>,
|
||||
pub username: String,
|
||||
pub pub_key: String,
|
||||
|
||||
#[serde(skip_deserializing)]
|
||||
pub created_at: String,
|
||||
#[serde(skip_deserializing)]
|
||||
pub updated_at: String,
|
||||
}
|
||||
|
||||
impl From<user::Model> for UserSerializer {
|
||||
fn from(model: user::Model) -> Self {
|
||||
Self {
|
||||
id: Some(model.id),
|
||||
username: model.username,
|
||||
pub_key: model.pub_key,
|
||||
created_at: model.created_at.to_string(),
|
||||
updated_at: model.updated_at.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UserSerializer {
|
||||
pub fn into_active_model(self) -> user::ActiveModel {
|
||||
user::ActiveModel {
|
||||
username: Set(self.username),
|
||||
pub_key: Set(self.pub_key),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_to_active_model(self, mut active_model: user::ActiveModel) -> user::ActiveModel { active_model.username = Set(self.username);
|
||||
active_model.pub_key = Set(self.pub_key);
|
||||
active_model
|
||||
}
|
||||
}
|
||||
@@ -1 +1,3 @@
|
||||
pub mod toolbox;
|
||||
pub mod password;
|
||||
pub mod ssh_auth;
|
||||
pub mod toolbox;
|
||||
|
||||
28
src/utils/password.rs
Normal file
28
src/utils/password.rs
Normal file
@@ -0,0 +1,28 @@
|
||||
// src/utils/password.rs
|
||||
use argon2::{
|
||||
password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString}, Algorithm, Argon2, Params,
|
||||
Version,
|
||||
};
|
||||
|
||||
/// Hache un password avec Argon2id
|
||||
/// Génère automatiquement un salt cryptographiquement sûr
|
||||
pub fn hash_password(password: &str) -> Result<String, argon2::password_hash::Error> {
|
||||
let salt = SaltString::generate(OsRng);
|
||||
let params = Params::new(65540, 18, 1, None)?;
|
||||
|
||||
let argon2 = Argon2::new(Algorithm::Argon2id, Version::V0x13, params);
|
||||
|
||||
argon2
|
||||
.hash_password(password.as_bytes(), &salt)
|
||||
.map(|hash| hash.to_string())
|
||||
}
|
||||
|
||||
/// Vérifie un password contre son hash
|
||||
pub fn verify_password(password: &str, hash: &str) -> Result<bool, argon2::password_hash::Error> {
|
||||
let parsed_hash = PasswordHash::new(hash)?;
|
||||
let argon2 = Argon2::default();
|
||||
|
||||
Ok(argon2
|
||||
.verify_password(password.as_bytes(), &parsed_hash)
|
||||
.is_ok())
|
||||
}
|
||||
61
src/utils/ssh_auth.rs
Normal file
61
src/utils/ssh_auth.rs
Normal file
@@ -0,0 +1,61 @@
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use chrono::{DateTime, Utc};
|
||||
use parking_lot::Mutex;
|
||||
use crate::utils::toolbox::ssh_generate_challenge;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct SshAuthManager {
|
||||
// session_id: String,
|
||||
challenges: Arc<Mutex<HashMap<String, SshAuthChallenge>>>,
|
||||
}
|
||||
|
||||
impl SshAuthManager {
|
||||
pub fn new() -> Self {
|
||||
let manager = Self {
|
||||
challenges: Arc::new(Mutex::new(HashMap::new()))
|
||||
};
|
||||
manager.start_cleanup_task();
|
||||
|
||||
manager
|
||||
}
|
||||
|
||||
fn start_cleanup_task(&self) {
|
||||
let challenges = self.challenges.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
let mut interval = tokio::time::interval(tokio::time::Duration::from_secs(30));
|
||||
loop {
|
||||
interval.tick().await;
|
||||
let mut map = challenges.lock();
|
||||
let now = Utc::now();
|
||||
map.retain(|_, challenge| {
|
||||
challenge.expires_at > now
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn add_auth_challenge(&self, session_id: String) -> SshAuthChallenge {
|
||||
let challenge = ssh_generate_challenge(1024);
|
||||
let auth_challenge = SshAuthChallenge {
|
||||
session_id: session_id.clone(),
|
||||
challenge,
|
||||
expires_at: Utc::now() + chrono::Duration::minutes(1)
|
||||
};
|
||||
|
||||
self.challenges.lock().insert(
|
||||
session_id,
|
||||
auth_challenge.clone()
|
||||
);
|
||||
|
||||
auth_challenge
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct SshAuthChallenge {
|
||||
session_id: String,
|
||||
challenge: String,
|
||||
expires_at: DateTime<Utc>
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
use rand::Rng;
|
||||
|
||||
pub fn number_of_cpus() -> usize {
|
||||
match std::thread::available_parallelism() {
|
||||
Ok(n) => n.get(),
|
||||
@@ -6,4 +8,31 @@ pub fn number_of_cpus() -> usize {
|
||||
1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_random(size: usize) -> Vec<u8> {
|
||||
let mut rng = rand::rng();
|
||||
let value = (0..size).map(|_| rng.random()).collect::<Vec<u8>>();
|
||||
value
|
||||
}
|
||||
|
||||
pub fn b64_encode(value: &[u8]) -> String {
|
||||
base64::Engine::encode(
|
||||
&base64::engine::general_purpose::STANDARD,
|
||||
value
|
||||
)
|
||||
}
|
||||
|
||||
pub fn b64_decode(value: &str) -> Result<Vec<u8>, std::io::Error> {
|
||||
base64::Engine::decode(
|
||||
&base64::engine::general_purpose::STANDARD,
|
||||
value
|
||||
).map_err(|e| {
|
||||
std::io::Error::new(std::io::ErrorKind::InvalidData, e)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn ssh_generate_challenge(size: usize) -> String {
|
||||
let challenge = generate_random(size);
|
||||
b64_encode(&challenge)
|
||||
}
|
||||
Reference in New Issue
Block a user