Add back-end part of ephemeral shares
This commit is contained in:
parent
523e2b9df9
commit
606e0a4c69
11 changed files with 404 additions and 30 deletions
160
Cargo.lock
generated
160
Cargo.lock
generated
|
@ -383,11 +383,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.79"
|
||||
version = "1.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
|
||||
checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -709,9 +711,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.8"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
|
||||
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
|
@ -936,6 +938,12 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "html-escape"
|
||||
version = "0.2.13"
|
||||
|
@ -1067,7 +1075,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1084,9 +1092,9 @@ checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
|
|||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.25"
|
||||
version = "0.1.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b"
|
||||
checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
@ -1114,9 +1122,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.138"
|
||||
version = "0.2.169"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
|
||||
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
|
@ -1201,7 +1209,7 @@ dependencies = [
|
|||
"libc",
|
||||
"log",
|
||||
"wasi",
|
||||
"windows-sys",
|
||||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1309,7 +1317,7 @@ dependencies = [
|
|||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-sys",
|
||||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1541,6 +1549,21 @@ dependencies = [
|
|||
"winreg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.17.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"getrandom",
|
||||
"libc",
|
||||
"spin",
|
||||
"untrusted",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.0"
|
||||
|
@ -1561,7 +1584,7 @@ dependencies = [
|
|||
"io-lifetimes",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1582,7 +1605,7 @@ version = "0.1.21"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1686,6 +1709,12 @@ dependencies = [
|
|||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.0"
|
||||
|
@ -1720,6 +1749,12 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
|
@ -1747,7 +1782,7 @@ dependencies = [
|
|||
"fastrand",
|
||||
"redox_syscall",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1844,7 +1879,7 @@ dependencies = [
|
|||
"signal-hook-registry",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"windows-sys",
|
||||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1974,6 +2009,12 @@ dependencies = [
|
|||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.3.1"
|
||||
|
@ -2161,13 +2202,38 @@ version = "0.42.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
"windows_aarch64_gnullvm 0.42.0",
|
||||
"windows_aarch64_msvc 0.42.0",
|
||||
"windows_i686_gnu 0.42.0",
|
||||
"windows_i686_msvc 0.42.0",
|
||||
"windows_x86_64_gnu 0.42.0",
|
||||
"windows_x86_64_gnullvm 0.42.0",
|
||||
"windows_x86_64_msvc 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.6",
|
||||
"windows_aarch64_msvc 0.52.6",
|
||||
"windows_i686_gnu 0.52.6",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc 0.52.6",
|
||||
"windows_x86_64_gnu 0.52.6",
|
||||
"windows_x86_64_gnullvm 0.52.6",
|
||||
"windows_x86_64_msvc 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2176,42 +2242,90 @@ version = "0.42.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.10.1"
|
||||
|
@ -2232,9 +2346,11 @@ dependencies = [
|
|||
"clap",
|
||||
"futures",
|
||||
"gloo-utils 0.2.0",
|
||||
"hex",
|
||||
"log",
|
||||
"rand",
|
||||
"reqwest",
|
||||
"ring",
|
||||
"serde",
|
||||
"tokio",
|
||||
"url-escape",
|
||||
|
|
10
Cargo.toml
10
Cargo.toml
|
@ -2,12 +2,12 @@
|
|||
name = "yarrr"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
description = "Template for starting a Yew project using Trunk"
|
||||
description = "An edgy web server for files"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/yewstack/yew-trunk-minimal-template"
|
||||
repository = "https://git.ngni.us/NGnius/yarrr"
|
||||
license = "MIT OR Apache-2.0"
|
||||
keywords = ["yew", "trunk"]
|
||||
categories = ["gui", "wasm", "web-programming"]
|
||||
keywords = ["ngnius", "yarrr"]
|
||||
categories = ["gui", "wasm"]
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[[bin]]
|
||||
|
@ -31,6 +31,7 @@ yew_icons = {version = "0.7", features = [
|
|||
"FeatherRefreshCcw"
|
||||
] }
|
||||
log = "0.4"
|
||||
hex = "0.4"
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
yew = { version = "0.20", features = [ "csr", "hydration" ] }
|
||||
|
@ -58,3 +59,4 @@ actix-web-httpauth = { version = "0.8" }
|
|||
clap = { version = "3.1.7", features = ["derive"] }
|
||||
url-escape = { version = "0.1.1" }
|
||||
rand = "0.8"
|
||||
ring = "0.17"
|
||||
|
|
1
aes256_test.key
Normal file
1
aes256_test.key
Normal file
|
@ -0,0 +1 @@
|
|||
7a56056a0276ab447bcb6b5a5162c3300b60325cc8eb25b1f39f3ab260539e2a
|
2
run.sh
2
run.sh
|
@ -1,4 +1,4 @@
|
|||
#!/bin/bash
|
||||
|
||||
#trunk build ./index.html && cargo run --bin backend -- localhost:7534
|
||||
trunk build ./index.html && cargo run --bin backend -- localhost:7534 --dir /mnt/nas/media -u user -p password --port 7534
|
||||
trunk build ./index.html && cargo run --bin backend -- localhost:7534 --dir /mnt/nas/media -u user -p password --port 7534 --key ./aes256_test.key
|
||||
|
|
|
@ -26,6 +26,12 @@ pub struct CliArgs {
|
|||
/// Local network port
|
||||
#[clap(long)]
|
||||
pub port: u16,
|
||||
/// Share links expiry time (seconds)
|
||||
#[clap(long)]
|
||||
pub expiry: Option<u64>,
|
||||
/// Share encyption key location
|
||||
#[clap(long)]
|
||||
pub key: Option<std::path::PathBuf>,
|
||||
}
|
||||
|
||||
impl CliArgs {
|
||||
|
@ -39,6 +45,11 @@ impl CliArgs {
|
|||
assert!(self.username.is_some(), "Credentials must be used to secure the hosted dir");
|
||||
assert!(self.password.is_some(), "Credentials must be used to secure the hosted dir");
|
||||
}
|
||||
if let Some(key) = &self.key {
|
||||
assert!(key.exists(), "Key file does not exist");
|
||||
assert!(key.is_file(), "Key file is not a file");
|
||||
assert!(std::fs::read_to_string(key).is_ok_and(|s| s.trim().chars().count() == 64), "Key file is not the expected size");
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ use crate::data::FileEntry;
|
|||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct FileRequest {
|
||||
mode: Option<DeliveryMode>,
|
||||
pub mode: Option<DeliveryMode>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone, Copy)]
|
||||
|
@ -18,14 +18,14 @@ pub enum DeliveryMode {
|
|||
}
|
||||
|
||||
impl DeliveryMode {
|
||||
fn disposition(self) -> actix_web::http::header::DispositionType {
|
||||
pub fn disposition(self) -> actix_web::http::header::DispositionType {
|
||||
match self {
|
||||
Self::Download => actix_web::http::header::DispositionType::Attachment,
|
||||
Self::Browser => actix_web::http::header::DispositionType::Inline,
|
||||
}
|
||||
}
|
||||
|
||||
fn default_from_ext(ext: Option<&std::ffi::OsStr>) -> Self {
|
||||
pub fn default_from_ext(ext: Option<&std::ffi::OsStr>) -> Self {
|
||||
let ext_str = ext.map(|x| x.to_str()).flatten();
|
||||
if let Some(ext) = ext_str {
|
||||
match ext {
|
||||
|
|
229
src/api/get_share.rs
Normal file
229
src/api/get_share.rs
Normal file
|
@ -0,0 +1,229 @@
|
|||
use std::io::{Read, Write};
|
||||
use std::sync::RwLock;
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use actix_web::{get, web, Responder};
|
||||
use actix_web_httpauth::extractors::basic::BasicAuth;
|
||||
|
||||
use ring::aead::{UnboundKey, SealingKey, OpeningKey, BoundKey, AES_256_GCM, NonceSequence, Nonce, NONCE_LEN, Aad};
|
||||
use bytes::Buf;
|
||||
|
||||
use super::get_files::{FileRequest, DeliveryMode};
|
||||
use crate::data::ShareInfo;
|
||||
|
||||
const DEFAULT_SHARE_TIME_S: u64 = 24 * 60 * 60;
|
||||
static AES256_KEY: RwLock<Option<[u8; 32]>> = RwLock::new(None);
|
||||
|
||||
fn get_key(key_file: impl AsRef<std::path::Path>) -> std::io::Result<UnboundKey> {
|
||||
if let Ok(Some(key)) = AES256_KEY.read().map(|k| k.clone()) {
|
||||
Ok(UnboundKey::new(&AES_256_GCM, &key).unwrap())
|
||||
} else {
|
||||
let mut lock = AES256_KEY.write().map_err(|e| std::io::Error::other(format!("write lock error: {}", e)))?;
|
||||
let hex_str = std::fs::read_to_string(key_file)?;
|
||||
let decoded_hex = hex::decode(hex_str.trim()).map_err(|e| std::io::Error::other(e))?;
|
||||
let mut buf_key = [0u8; 32];
|
||||
if buf_key.len() != decoded_hex.len() {
|
||||
return Err(std::io::Error::other("Length of bytes read for share encryption key did not match expected"));
|
||||
}
|
||||
for i in 0..buf_key.len() {
|
||||
buf_key[i] = decoded_hex[i];
|
||||
}
|
||||
*lock = Some(buf_key.clone());
|
||||
Ok(UnboundKey::new(&AES_256_GCM, &buf_key).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
fn unix_time_s_now() -> u64 {
|
||||
std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs()
|
||||
}
|
||||
|
||||
struct NonceGenerator {
|
||||
offset: u64,
|
||||
seed: String,
|
||||
}
|
||||
|
||||
impl NonceSequence for NonceGenerator {
|
||||
fn advance(&mut self) -> Result<Nonce, ring::error::Unspecified> {
|
||||
if self.offset == u64::MAX {
|
||||
Err(ring::error::Unspecified)
|
||||
} else {
|
||||
let mut ctx = ring::digest::Context::new(&ring::digest::SHA256);
|
||||
let seed_bytes = self.seed.as_bytes();
|
||||
let real_offset = self.offset as usize % seed_bytes.len();
|
||||
ctx.update(&seed_bytes[real_offset..]);
|
||||
ctx.update(&seed_bytes[..real_offset]);
|
||||
ctx.update(&(seed_bytes.len() as u64).to_le_bytes());
|
||||
ctx.update(&(real_offset as u64).to_le_bytes());
|
||||
let digest = ctx.finish();
|
||||
self.offset += 1;
|
||||
Ok(Nonce::try_assume_unique_for_key(&digest.as_ref()[0..NONCE_LEN]).unwrap())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SharePayload {
|
||||
path: String,
|
||||
expiry: u64,
|
||||
}
|
||||
|
||||
impl SharePayload {
|
||||
pub fn from_hex(hex_str: &str, key_file: impl AsRef<std::path::Path>) -> std::io::Result<Self> {
|
||||
let args = super::CliArgs::get();
|
||||
let username = args.username.unwrap_or_else(|| "nouser".to_string());
|
||||
// nonce-prefixed encrypted data
|
||||
let mut bytes = VecDeque::from(hex::decode(hex_str).map_err(|e| std::io::Error::other(e))?);
|
||||
// pull out nonce from data payload
|
||||
let mut buf_u64 = [0u8; 8];
|
||||
for i in 0..buf_u64.len() {
|
||||
buf_u64[i] = bytes.pop_front().unwrap();
|
||||
}
|
||||
// decrypt
|
||||
let mut bytes = Vec::from(bytes);
|
||||
let nonce_offset = u64::from_le_bytes(buf_u64);
|
||||
let nonce_gen = NonceGenerator {
|
||||
offset: nonce_offset,
|
||||
seed: args.password.unwrap_or_else(|| "you fool".to_string()),
|
||||
};
|
||||
let mut key = OpeningKey::new(get_key(key_file)?, nonce_gen);
|
||||
key.open_in_place(Aad::from(username), &mut bytes).map_err(|e| std::io::Error::other(format!("Failed to decrypt: {}", e)))?;
|
||||
Self::from_bytes(&mut bytes.reader())
|
||||
}
|
||||
|
||||
fn from_bytes(bytes: &mut impl Read) -> std::io::Result<Self> {
|
||||
let mut buf_u64 = [0u8; 8];
|
||||
let size = bytes.read(&mut buf_u64)?;
|
||||
if size != buf_u64.len() {
|
||||
return Err(std::io::Error::other("Length of bytes read for path length did not match expected"));
|
||||
}
|
||||
let path_len = u64::from_le_bytes(buf_u64);
|
||||
let mut buf_path = vec![0u8; path_len as _];
|
||||
let size = bytes.read(&mut buf_path)?;
|
||||
if size != buf_path.len() {
|
||||
return Err(std::io::Error::other("Length of bytes read for path did not match expected"));
|
||||
}
|
||||
let path = String::from_utf8(buf_path).map_err(|e| std::io::Error::other(e))?;
|
||||
// reuse buf_u64
|
||||
let size = bytes.read(&mut buf_u64)?;
|
||||
if size != buf_u64.len() {
|
||||
return Err(std::io::Error::other("Length of bytes read for expiry timestamp did not match expected"));
|
||||
}
|
||||
let expiry = u64::from_le_bytes(buf_u64);
|
||||
Ok(Self {
|
||||
path,
|
||||
expiry,
|
||||
})
|
||||
}
|
||||
|
||||
fn to_hex(&self, key_file: impl AsRef<std::path::Path>) -> std::io::Result<String> {
|
||||
let args = super::CliArgs::get();
|
||||
let username = args.username.unwrap_or_else(|| "nouser".to_string());
|
||||
let mut bytes = Vec::new();
|
||||
let nonce_offset: u64 = rand::random();
|
||||
|
||||
self.to_bytes(&mut bytes)?;
|
||||
// encrypt
|
||||
let nonce_gen = NonceGenerator {
|
||||
offset: nonce_offset,
|
||||
seed: args.password.unwrap_or_else(|| "you fool".to_string()),
|
||||
};
|
||||
let mut key = SealingKey::new(get_key(key_file)?, nonce_gen);
|
||||
key.seal_in_place_append_tag(Aad::from(username), &mut bytes).map_err(|e| std::io::Error::other(format!("Failed to encrypt: {}", e)))?;
|
||||
// add nonce hint
|
||||
let mut bytes = VecDeque::from(bytes);
|
||||
let buf_u64 = nonce_offset.to_le_bytes();
|
||||
for i in (0..buf_u64.len()).rev() {
|
||||
bytes.push_front(buf_u64[i]);
|
||||
}
|
||||
let bytes = Vec::from(bytes);
|
||||
Ok(hex::encode(bytes))
|
||||
}
|
||||
|
||||
fn to_bytes(&self, bytes: &mut impl Write) -> std::io::Result<usize> {
|
||||
let path_bytes = self.path.as_bytes();
|
||||
let path_len: u64 = path_bytes.len() as _;
|
||||
let mut size = bytes.write(&path_len.to_le_bytes())?;
|
||||
size += bytes.write(path_bytes)?;
|
||||
size += bytes.write(&self.expiry.to_le_bytes())?;
|
||||
Ok(size)
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/share/{encrypted:.*}")]
|
||||
pub async fn shared_file(encrypted: web::Path<String>, query: web::Query<FileRequest>) -> std::io::Result<impl Responder> {
|
||||
let args = super::CliArgs::get();
|
||||
if args.key.is_none() {
|
||||
return Err(std::io::Error::new(std::io::ErrorKind::PermissionDenied, "Share Authentication failed"));
|
||||
}
|
||||
let enc_key_file = args.key.unwrap();
|
||||
let payload = SharePayload::from_hex(&encrypted, enc_key_file)?;
|
||||
let time_now = unix_time_s_now();
|
||||
if payload.expiry < time_now {
|
||||
return Err(std::io::Error::new(std::io::ErrorKind::PermissionDenied, "Share expired"));
|
||||
}
|
||||
let root = args.dir.unwrap();
|
||||
let filepath = root.join(&*payload.path);
|
||||
let req_disposition = query.mode.unwrap_or_else(
|
||||
|| DeliveryMode::default_from_ext(filepath.extension())
|
||||
).disposition();
|
||||
log::debug!("file PATH: {}", filepath.display());
|
||||
Ok(
|
||||
actix_files::NamedFile::open_async(&filepath).await?
|
||||
.prefer_utf8(false)
|
||||
.set_content_disposition(
|
||||
actix_web::http::header::ContentDisposition {
|
||||
disposition: req_disposition,
|
||||
parameters: vec![
|
||||
/*actix_web::http::header::DispositionParam::FilenameExt(
|
||||
actix_web::http::header::ExtendedValue {
|
||||
charset: actix_web::http::header::Charset::Iso_8859_1,
|
||||
language_tag: None,
|
||||
value: filepath.file_name()
|
||||
.map(
|
||||
|name| name
|
||||
.to_string_lossy()
|
||||
.as_bytes()
|
||||
.to_vec()
|
||||
).unwrap_or_else(|| Vec::with_capacity(0)),
|
||||
}
|
||||
),*/
|
||||
actix_web::http::header::DispositionParam::Filename(
|
||||
filepath.file_name()
|
||||
.map(
|
||||
|name| name
|
||||
.to_string_lossy()
|
||||
.to_string()
|
||||
).unwrap_or_else(|| "unknown".into()),
|
||||
)
|
||||
],
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
#[get("/api/share/{path:.*}")]
|
||||
pub async fn share(path: web::Path<String>, auth: BasicAuth) -> std::io::Result<impl Responder> {
|
||||
let args = super::CliArgs::get();
|
||||
if !args.authenticate(auth.user_id(), auth.password().unwrap_or("")).await {
|
||||
return Err(std::io::Error::new(std::io::ErrorKind::PermissionDenied, "Basic Authentication failed"))
|
||||
}
|
||||
if args.key.is_none() {
|
||||
return Err(std::io::Error::new(std::io::ErrorKind::PermissionDenied, "Share Authentication failed"));
|
||||
}
|
||||
let enc_key_file = args.key.unwrap();
|
||||
let root = args.dir.unwrap();
|
||||
let filepath = root.join(&*path);
|
||||
if !(filepath.exists() && filepath.is_file()) {
|
||||
return Err(std::io::Error::new(std::io::ErrorKind::NotFound, "Invalid path"))
|
||||
}
|
||||
// build payload
|
||||
let expiry_time = unix_time_s_now() + args.expiry.unwrap_or(DEFAULT_SHARE_TIME_S);
|
||||
let hex_str = SharePayload {
|
||||
path: path.to_owned(),
|
||||
expiry: expiry_time,
|
||||
}.to_hex(enc_key_file)?;
|
||||
let share_info = ShareInfo {
|
||||
path: std::path::PathBuf::from(&*path),
|
||||
url_path: format!("/share/{}", &hex_str),
|
||||
full_url: format!("{}/share/{}", args.domain, &hex_str),
|
||||
};
|
||||
Ok(web::Json(share_info))
|
||||
}
|
|
@ -4,6 +4,7 @@ mod get_banner;
|
|||
mod get_files;
|
||||
mod get_index;
|
||||
mod get_resources;
|
||||
mod get_share;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub use get_args::CliArgs;
|
||||
|
@ -11,3 +12,4 @@ pub use get_banner::bruce;
|
|||
pub use get_files::{dir, file};
|
||||
pub use get_index::{index_auth, index_no_auth, IndexPage};
|
||||
pub use get_resources::resource;
|
||||
pub use get_share::{share, shared_file};
|
||||
|
|
|
@ -6,6 +6,7 @@ use yarrr::api::{
|
|||
resource,
|
||||
dir, file,
|
||||
bruce,
|
||||
share, shared_file,
|
||||
};
|
||||
|
||||
|
||||
|
@ -25,6 +26,8 @@ async fn main() -> std::io::Result<()> {
|
|||
.service(resource)
|
||||
.service(dir)
|
||||
.service(file)
|
||||
.service(share)
|
||||
.service(shared_file)
|
||||
})
|
||||
.bind(("127.0.0.1", args.port))?
|
||||
.run()
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
mod file_entry;
|
||||
mod share_info;
|
||||
|
||||
pub use file_entry::FileEntry;
|
||||
pub use share_info::ShareInfo;
|
||||
|
|
8
src/data/share_info.rs
Normal file
8
src/data/share_info.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct ShareInfo {
|
||||
pub path: std::path::PathBuf,
|
||||
pub url_path: String,
|
||||
pub full_url: String,
|
||||
}
|
Loading…
Add table
Reference in a new issue