diff options
| -rw-r--r-- | Cargo.lock | 93 | ||||
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rwxr-xr-x | bin/dev-server | 2 | ||||
| -rw-r--r-- | src/routes.rs | 93 |
4 files changed, 146 insertions, 43 deletions
@@ -95,18 +95,47 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] +name = "bcrypt" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523ab528ce3a7ada6597f8ccf5bd8d85ebe26d5edf311cad4d1d3cfb2d357ac6" +dependencies = [ + "base64", + "blowfish", + "getrandom", + "subtle", + "zeroize", +] + +[[package]] name = "bitflags" version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" [[package]] +name = "blowfish" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7" +dependencies = [ + "byteorder", + "cipher", +] + +[[package]] name = "bumpalo" version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" [[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] name = "bytes" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -153,6 +182,16 @@ dependencies = [ ] [[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 = "colorchoice" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -219,6 +258,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] name = "env_filter" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -280,6 +329,7 @@ name = "files" version = "0.1.0" dependencies = [ "anyhow", + "bcrypt", "chrono", "const_format", "env_logger", @@ -368,6 +418,16 @@ dependencies = [ ] [[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] name = "getrandom" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -576,6 +636,15 @@ dependencies = [ ] [[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array", +] + +[[package]] name = "ipnet" version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -995,6 +1064,12 @@ dependencies = [ ] [[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] name = "syn" version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1129,6 +1204,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] name = "unicode-ident" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1159,6 +1240,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] name = "want" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1438,6 +1525,12 @@ dependencies = [ ] [[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + +[[package]] name = "zmij" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -6,6 +6,7 @@ edition = "2021" [dependencies] anyhow = "1.0" +bcrypt = "0.19" chrono = "0.4" const_format = "0.2" env_logger = "0.11" diff --git a/bin/dev-server b/bin/dev-server index b45b595..5938b9f 100755 --- a/bin/dev-server +++ b/bin/dev-server @@ -3,7 +3,7 @@ set -euo pipefail export HOST="127.0.0.1" export PORT="8080" -export KEY="1234" +export KEY='$2y$12$1k/1ZFalf8ZNV1pGoLRU0e3ppvCgPxevp9ay8vtJUxhECZuoVMgBu' export DB="db.sqlite3" export FILES_DIR="files" diff --git a/src/routes.rs b/src/routes.rs index ca1cc41..f5deeb4 100644 --- a/src/routes.rs +++ b/src/routes.rs @@ -59,52 +59,61 @@ async fn upload_file( authorized_key: String, files_dir: &Path, ) -> Result<Response<BoxBody<Bytes, std::io::Error>>> { - let key = get_header(&request, "X-Key"); - if key != Some(authorized_key) { - log::info!("Unauthorized file upload"); - Ok(response(StatusCode::UNAUTHORIZED, "Unauthorized")) - } else { - let file_id = model::generate_file_id(); - let filename = get_header(&request, "X-Filename").map(|s| util::sanitize_filename(&s)); - let expiration_days: Option<i64> = - get_header(&request, "X-Expiration").and_then(|s| s.parse().ok()); - let content_length: Option<usize> = - get_header(&request, "Content-Length").and_then(|s| s.parse().ok()); - - match (filename, expiration_days, content_length) { - (Some(filename), Some(expiration_days), Some(content_length)) => { - let _ = fs::create_dir(files_dir).await; - let path = files_dir.join(&file_id); - let mut file = File::create(&path).await.unwrap(); - - let mut incoming = request.into_body(); - while let Some(frame) = incoming.frame().await { - if let Ok(data) = frame { - let _ = file.write_all(&data.into_data().unwrap()).await; - let _ = file.flush().await; - } - } - - let file = model::File { - id: file_id.clone(), - name: filename, - expires_at: model::local_time().add(Duration::days(expiration_days)), - content_length, - }; - - match db::files::insert(&db_conn, file.clone()).await { - Ok(_) => Ok(response(StatusCode::OK, file_id)), - Err(msg) => { - log::error!("Insert file: {msg}"); - if let Err(msg) = fs::remove_file(path).await { - log::error!("Remove file: {msg}"); + match get_header(&request, "X-Key") { + None => { + log::info!("Unauthorized file upload"); + Ok(response(StatusCode::UNAUTHORIZED, "Unauthorized")) + } + Some(key) => match bcrypt::verify(key, &authorized_key) { + Ok(true) => { + let file_id = model::generate_file_id(); + let filename = + get_header(&request, "X-Filename").map(|s| util::sanitize_filename(&s)); + let expiration_days: Option<i64> = + get_header(&request, "X-Expiration").and_then(|s| s.parse().ok()); + let content_length: Option<usize> = + get_header(&request, "Content-Length").and_then(|s| s.parse().ok()); + + match (filename, expiration_days, content_length) { + (Some(filename), Some(expiration_days), Some(content_length)) => { + let _ = fs::create_dir(files_dir).await; + let path = files_dir.join(&file_id); + let mut file = File::create(&path).await.unwrap(); + + let mut incoming = request.into_body(); + while let Some(frame) = incoming.frame().await { + if let Ok(data) = frame { + let _ = file.write_all(&data.into_data().unwrap()).await; + let _ = file.flush().await; + } + } + + let file = model::File { + id: file_id.clone(), + name: filename, + expires_at: model::local_time().add(Duration::days(expiration_days)), + content_length, }; - Ok(internal_server_error()) + + match db::files::insert(&db_conn, file.clone()).await { + Ok(_) => Ok(response(StatusCode::OK, file_id)), + Err(msg) => { + log::error!("Insert file: {msg}"); + if let Err(msg) = fs::remove_file(path).await { + log::error!("Remove file: {msg}"); + }; + Ok(internal_server_error()) + } + } } + _ => Ok(bad_request()), } } - _ => Ok(bad_request()), - } + _ => { + log::info!("Unauthorized file upload"); + Ok(response(StatusCode::UNAUTHORIZED, "Unauthorized")) + } + }, } } |
