diff --git a/Cargo.lock b/Cargo.lock index dfd9dab..0e71375 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -166,23 +166,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "actix-session" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d324d2a6e670f8746ae64f333c2c0fe856bdf624bcb72fb56e250e62a7e9a85f" -dependencies = [ - "actix-service", - "actix-utils", - "actix-web", - "anyhow", - "async-trait", - "derive_more", - "serde", - "serde_json", - "tracing", -] - [[package]] name = "actix-utils" version = "3.0.0" @@ -279,15 +262,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "aead" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" -dependencies = [ - "generic-array", -] - [[package]] name = "aes" version = "0.7.5" @@ -300,20 +274,6 @@ dependencies = [ "opaque-debug", ] -[[package]] -name = "aes-gcm" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6" -dependencies = [ - "aead", - "aes", - "cipher", - "ctr", - "ghash", - "subtle", -] - [[package]] name = "ahash" version = "0.7.6" @@ -349,23 +309,6 @@ dependencies = [ "alloc-no-stdlib", ] -[[package]] -name = "anyhow" -version = "1.0.62" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1485d4d2cc45e7b201ee3767015c96faa5904387c9d87c6efdd0fb511f12d305" - -[[package]] -name = "argon2" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db4ce4441f99dbd377ca8a8f57b698c44d0d6e712d8329b5040da5a64aa1ce73" -dependencies = [ - "base64ct", - "blake2", - "password-hash 0.4.2", -] - [[package]] name = "askama" version = "0.11.1" @@ -425,17 +368,6 @@ dependencies = [ "toml", ] -[[package]] -name = "async-trait" -version = "0.1.53" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed6aa3524a2dfcf9fe180c51eae2b58738348d819517ceadf95789c51fff7600" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "atty" version = "0.2.14" @@ -471,15 +403,6 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" -[[package]] -name = "blake2" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9cf849ee05b2ee5fba5e36f97ff8ec2533916700fc0758d40d92136a42f3388" -dependencies = [ - "digest", -] - [[package]] name = "block-buffer" version = "0.10.2" @@ -620,14 +543,7 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94d4706de1b0fa5b132270cddffa8585166037822e260a944fe161acd137ca05" dependencies = [ - "aes-gcm", - "base64", - "hkdf", - "hmac", "percent-encoding", - "rand", - "sha2", - "subtle", "time", "version_check", ] @@ -680,15 +596,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "ctr" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" -dependencies = [ - "cipher", -] - [[package]] name = "darling" version = "0.14.1" @@ -861,16 +768,6 @@ dependencies = [ "wasi 0.10.2+wasi-snapshot-preview1", ] -[[package]] -name = "ghash" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99" -dependencies = [ - "opaque-debug", - "polyval", -] - [[package]] name = "h2" version = "0.3.13" @@ -911,15 +808,6 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -[[package]] -name = "hkdf" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" -dependencies = [ - "hmac", -] - [[package]] name = "hmac" version = "0.12.1" @@ -1290,17 +1178,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "password-hash" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" -dependencies = [ - "base64ct", - "rand_core", - "subtle", -] - [[package]] name = "paste" version = "1.0.7" @@ -1315,7 +1192,7 @@ checksum = "271779f35b581956db91a3e55737327a03aa051e90b1c47aeb189508533adfd7" dependencies = [ "digest", "hmac", - "password-hash 0.3.2", + "password-hash", "sha2", ] @@ -1343,18 +1220,6 @@ version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" -[[package]] -name = "polyval" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" -dependencies = [ - "cfg-if", - "cpufeatures", - "opaque-debug", - "universal-hash", -] - [[package]] name = "ppv-lite86" version = "0.2.16" @@ -1783,13 +1648,10 @@ dependencies = [ "actix", "actix-files", "actix-http", - "actix-session", "actix-web", "actix-web-actors", - "argon2", "askama", "askama_actix", - "base64", "bytes", "bytesize", "crc32fast", @@ -1801,7 +1663,6 @@ dependencies = [ "log", "mime", "mnemonic", - "password-hash 0.4.2", "pin-project-lite", "rand", "sanitise-file-name", @@ -1851,16 +1712,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" -[[package]] -name = "universal-hash" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" -dependencies = [ - "generic-array", - "subtle", -] - [[package]] name = "url" version = "2.2.2" diff --git a/Cargo.toml b/Cargo.toml index 58d0229..c91dee1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,13 +9,10 @@ license = "MIT" actix = "0.13" actix-files = "0.6.0" actix-http = "3.0.4" -actix-session = { version = "0.7.1", features = ["cookie-session"] } actix-web = "4.0.1" actix-web-actors = "4.1.0" -argon2 = "0.4.1" askama = "0.11.1" askama_actix = "0.13" -base64 = "0.13" bytes = "1.1.0" bytesize = "1.1.0" crc32fast = "1.3.2" @@ -27,7 +24,6 @@ jsondb = "0.4.0" log = "0.4" mime = "0.3.16" mnemonic = "1.0.1" -password-hash = { version = "0.4.2", features = ["alloc"] } pin-project-lite = "0.2.9" rand = "0.8.5" sanitise-file-name = "1.0.0" diff --git a/README.md b/README.md index f71c743..525eae6 100644 --- a/README.md +++ b/README.md @@ -24,16 +24,6 @@ ## configuration transbeam is configured with the following environment variables: -- `TRANSBEAM_ADMIN_PASSWORD_HASH`: Argon2 hash of a password for - accessing the admin interface. To generate: - ```bash - echo -n 'Password: '; head -1 | tr -d '\n' | argon2 $(openssl rand -base64 32) -id -t 2 -m 14 -p 1 -e - ``` -- `TRANSBEAM_COOKIE_SECRET`: Base64-encoded cryptographic random data - for encrypting private session cookies. To generate: - ```bash - openssl rand -base64 64 | tr -d '\n' - ``` - `TRANSBEAM_STORAGE_DIR`: path where uploaded files should be stored (default: `./storage`) - `TRANSBEAM_STATIC_DIR`: path where the web app's static files live diff --git a/src/main.rs b/src/main.rs index 99a4b11..8aec7b1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,23 +4,20 @@ mod store; mod upload; mod zip; -use std::{fmt::Debug, path::PathBuf, str::FromStr, ops::Deref}; +use std::{fmt::Debug, path::PathBuf, str::FromStr}; use actix_http::StatusCode; -use actix_session::{SessionMiddleware, storage::CookieSessionStore, Session}; use actix_web::{ error::InternalError, get, middleware::Logger, post, web, App, HttpRequest, HttpResponse, - HttpServer, Responder, cookie, + HttpServer, Responder, }; use actix_web_actors::ws; -use argon2::{Argon2, PasswordVerifier}; use askama_actix::{Template, TemplateToResponse}; use bytesize::ByteSize; use log::{error, warn}; -use password_hash::PasswordHashString; use serde::{Deserialize, Serialize}; -use state::{StateDb, prelude::SizedFile}; -use store::{StoredFile, StoredFiles}; +use state::StateDb; +use store::StoredFile; use tokio::fs::File; const APP_NAME: &str = "transbeam"; @@ -43,7 +40,6 @@ struct Config { reverse_proxy: bool, mnemonic_codes: bool, cachebuster: String, - admin_password_hash: PasswordHashString, } pub fn get_ip_addr(req: &HttpRequest, reverse_proxy: bool) -> String { @@ -76,67 +72,6 @@ async fn index(data: web::Data) -> impl Responder { } } -#[derive(Template)] -#[template(path = "admin/signed_out.html")] -struct SignedOutAdminPage { - cachebuster: String, - base_url: String, - incorrect_password: bool, -} - -#[derive(Template)] -#[template(path = "admin/signed_in.html")] -struct AdminPage<'a> { - cachebuster: String, - base_url: String, - stored_files: &'a StoredFiles, -} - -#[get("/admin")] -async fn admin_panel(data: web::Data, session: Session) -> actix_web::Result { - if let Some(true) = session.get::("admin")? { - Ok(AdminPage { - cachebuster: data.config.cachebuster.clone(), - base_url: data.config.base_url.clone(), - stored_files: data.state.read().await.deref(), - }.to_response()) - } else { - Ok(SignedOutAdminPage { - cachebuster: data.config.cachebuster.clone(), - base_url: data.config.base_url.clone(), - incorrect_password: false, - }.to_response()) - } -} - -#[derive(Deserialize)] -struct AdminPanelSignin { - password: String, -} - -#[post("/admin")] -async fn admin_signin(req: HttpRequest, data: web::Data, form: web::Form, session: Session) -> actix_web::Result { - if Argon2::default().verify_password(form.password.as_bytes(), &data.config.admin_password_hash.password_hash()).is_ok() { - session.insert("admin", true)?; - Ok(AdminPage { - cachebuster: data.config.cachebuster.clone(), - base_url: data.config.base_url.clone(), - stored_files: data.state.read().await.deref(), - }.to_response()) - } else { - let ip_addr = get_ip_addr(&req, data.config.reverse_proxy); - log_auth_failure(&ip_addr); - let mut resp = SignedOutAdminPage { - cachebuster: data.config.cachebuster.clone(), - base_url: data.config.base_url.clone(), - incorrect_password: true, - }.to_response(); - *resp.status_mut() = StatusCode::FORBIDDEN; - - Err(InternalError::from_response("Incorrect password", resp).into()) - } -} - #[derive(Deserialize)] struct DownloadRequest { code: String, @@ -372,15 +307,6 @@ async fn main() -> std::io::Result<()> { env_or::("TRANSBEAM_MAX_STORAGE_SIZE", ByteSize(64 * bytesize::GB)).as_u64(); let upload_password: String = env_or_panic("TRANSBEAM_UPLOAD_PASSWORD"); let cachebuster: String = env_or_else("TRANSBEAM_CACHEBUSTER", String::new); - let admin_password_hash: PasswordHashString = env_or_panic("TRANSBEAM_ADMIN_PASSWORD_HASH"); - - let cookie_secret_base64: String = env_or_panic("TRANSBEAM_COOKIE_SECRET"); - let cookie_key = cookie::Key::from( - &base64::decode(&cookie_secret_base64) - .unwrap_or_else( - |_| panic!("Value {} for TRANSBEAM_COOKIE_SECRET is not valid base64", cookie_secret_base64) - ) - ); let state_file: PathBuf = match std::env::var("TRANSBEAM_STATE_FILE") { Ok(v) => v @@ -410,7 +336,6 @@ async fn main() -> std::io::Result<()> { reverse_proxy, mnemonic_codes, cachebuster, - admin_password_hash, }, }); data.cleanup().await?; @@ -424,15 +349,12 @@ async fn main() -> std::io::Result<()> { } else { Logger::default() }) - .wrap(SessionMiddleware::new(CookieSessionStore::default(), cookie_key.clone())) .service(index) .service(handle_download) .service(download_info) .service(handle_upload) .service(check_upload_password) .service(upload_limits) - .service(admin_panel) - .service(admin_signin) .service(actix_files::Files::new("/", static_dir.clone())) }); diff --git a/src/state.rs b/src/state.rs index 352971a..b7e6635 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,6 +1,6 @@ use jsondb::JsonDb; -pub mod prelude { +mod prelude { pub use std::collections::HashMap; pub use jsondb::Schema; @@ -8,17 +8,8 @@ pub mod prelude { pub use serde_with::serde_as; pub use serde_with::skip_serializing_none; pub use time::OffsetDateTime; - - pub trait SizedFile { - fn size(&self) -> u64; - - fn formatted_size(&self) -> String { - bytesize::to_string(self.size(), false).replace(" ", "") - } - } } - mod v0; pub mod v1 { @@ -51,9 +42,6 @@ pub mod v1 { } } } - impl SizedFile for UploadedFile { - fn size(&self) -> u64 { self.size } - } #[derive(Debug, Clone, Deserialize, Serialize)] pub struct FileSet { @@ -82,9 +70,6 @@ pub mod v1 { pub expiry: OffsetDateTime, pub contents: Option, } - impl SizedFile for StoredFile { - fn size(&self) -> u64 { self.size } - } #[derive(Debug, Clone, Deserialize, Serialize)] pub struct StoredFileWithPassword { diff --git a/static/css/transbeam.css b/static/css/transbeam.css index 13e373e..44dedec 100644 --- a/static/css/transbeam.css +++ b/static/css/transbeam.css @@ -234,14 +234,7 @@ summary { td.file_size { text-align: right; -} - -td.file_size, th.file_size { - width: 6rem; -} - -td.file_expiry, th.file_expiry { - width: 10rem; + width: 5.12rem; } td.file_name { diff --git a/templates/admin/signed_in.html b/templates/admin/signed_in.html deleted file mode 100644 index 710ba4d..0000000 --- a/templates/admin/signed_in.html +++ /dev/null @@ -1,21 +0,0 @@ -{% extends "base.html" %} - -{% block body %} -
-

Admin

- - - - {% for (code, entry) in stored_files.0.iter() %} - - - - - - {% endfor %} -
Name - Size - Expiry -
{{ entry.file.name }}{{ entry.file.formatted_size() }}{{ entry.file.expiry.format(DATE_DISPLAY_FORMAT).unwrap() }}
-
-{% endblock %} diff --git a/templates/admin/signed_out.html b/templates/admin/signed_out.html deleted file mode 100644 index dd5f725..0000000 --- a/templates/admin/signed_out.html +++ /dev/null @@ -1,18 +0,0 @@ -{% extends "base.html" %} - -{% block body %} -
-

Sign In

- {% if incorrect_password %} -

Incorrect Password

- {% endif %} -
-
- -
- -
-
-{% endblock %} diff --git a/templates/download.html b/templates/download.html index 91fbc48..1c6e13c 100644 --- a/templates/download.html +++ b/templates/download.html @@ -4,7 +4,7 @@ {% block og_title %}{{ info.file.name }}{% endblock %} {% block og_description -%} - {% let formatted_total_size = info.file.formatted_size() -%} + {% let formatted_total_size = bytesize::to_string(info.file.size.clone(), false).replace(" ", "") -%} {% match info.file.contents -%} {% when Some with (files) -%} {{ files.files.len() }} files, {{ formatted_total_size }} total @@ -26,7 +26,7 @@ {% block body %}
{{ info.file.name }}
-
{{ info.file.formatted_size() }}
+
{{ bytesize::to_string(info.file.size.clone(), false).replace(" ", "") }}
Expires {{ info.file.expiry.format(DATE_DISPLAY_FORMAT).unwrap() }}
@@ -39,7 +39,7 @@ {% let offsets = info.offsets.as_ref().unwrap() %} {% for f in files.files %} - {{ f.formatted_size() }} + {{ bytesize::to_string(f.size.clone(), false).replace(" ", "") }} {{ f.name }}