forked from jmug/stoatchat
feat(services/autumn): file uploads
feat(services/autumn): deduplicate uploads feat(services/autumn): ClamAV support
This commit is contained in:
@@ -14,5 +14,7 @@ typenum = "1.17.0"
|
||||
aws-config = "1.5.5"
|
||||
aws-sdk-s3 = { version = "1.46.0", features = ["behavior-version-latest"] }
|
||||
|
||||
revolt-config = { version = "0.7.16", path = "../config" }
|
||||
revolt-config = { version = "0.7.16", path = "../config", features = [
|
||||
"report-macros",
|
||||
] }
|
||||
revolt-result = { version = "0.7.16", path = "../result" }
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
use std::io::Write;
|
||||
|
||||
use aes_gcm::{aead::AeadMutInPlace, Aes256Gcm, Key, KeyInit, Nonce};
|
||||
use revolt_config::{config, FilesS3};
|
||||
use aes_gcm::{
|
||||
aead::{AeadCore, AeadMutInPlace, OsRng},
|
||||
Aes256Gcm, Key, KeyInit, Nonce,
|
||||
};
|
||||
use revolt_config::{config, report_internal_error, FilesS3};
|
||||
use revolt_result::{create_error, Result};
|
||||
|
||||
use aws_sdk_s3::{
|
||||
@@ -11,7 +14,8 @@ use aws_sdk_s3::{
|
||||
|
||||
use base64::prelude::*;
|
||||
|
||||
pub static AUTHENTICATION_TAG_SIZE_BYTES: usize = 16;
|
||||
/// Size of the authentication tag in the buffer
|
||||
pub const AUTHENTICATION_TAG_SIZE_BYTES: usize = 16;
|
||||
|
||||
/// Create an S3 client
|
||||
pub fn create_client(s3_config: FilesS3) -> Client {
|
||||
@@ -35,7 +39,7 @@ pub fn create_client(s3_config: FilesS3) -> Client {
|
||||
|
||||
/// Create an AES-256-GCM cipher
|
||||
pub fn create_cipher(key: &str) -> Aes256Gcm {
|
||||
let key = &BASE64_STANDARD.decode(key).unwrap()[..];
|
||||
let key = &BASE64_STANDARD.decode(key).expect("valid base64 string")[..];
|
||||
let key: &Key<Aes256Gcm> = key.into();
|
||||
Aes256Gcm::new(key)
|
||||
}
|
||||
@@ -46,23 +50,14 @@ pub async fn fetch_from_s3(bucket_id: &str, path: &str, nonce: &str) -> Result<V
|
||||
let client = create_client(config.files.s3);
|
||||
|
||||
// Send a request for the file
|
||||
let mut obj = client
|
||||
.get_object()
|
||||
.bucket(bucket_id)
|
||||
.key(path)
|
||||
.send()
|
||||
.await
|
||||
.inspect_err(|err| {
|
||||
revolt_config::capture_error(err);
|
||||
})
|
||||
.map_err(|_| create_error!(InternalError))?;
|
||||
let mut obj =
|
||||
report_internal_error!(client.get_object().bucket(bucket_id).key(path).send().await)?;
|
||||
|
||||
// Read the file from remote
|
||||
let mut buf = vec![];
|
||||
while let Some(bytes) = obj.body.next().await {
|
||||
let data = bytes.map_err(|_| create_error!(InternalError))?;
|
||||
buf.write_all(&data)
|
||||
.map_err(|_| create_error!(InternalError))?;
|
||||
let data = report_internal_error!(bytes)?;
|
||||
report_internal_error!(buf.write_all(&data))?;
|
||||
// is there a more efficient way to do this?
|
||||
// we just want the Vec<u8>
|
||||
}
|
||||
@@ -78,3 +73,33 @@ pub async fn fetch_from_s3(bucket_id: &str, path: &str, nonce: &str) -> Result<V
|
||||
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
/// Encrypt and upload a file to S3 (returning its nonce/IV)
|
||||
pub async fn upload_to_s3(bucket_id: &str, path: &str, buf: &[u8]) -> Result<String> {
|
||||
let config = config().await;
|
||||
let client = create_client(config.files.s3);
|
||||
|
||||
// Generate a nonce
|
||||
let nonce = Aes256Gcm::generate_nonce(&mut OsRng);
|
||||
|
||||
// Extend the buffer for in-place encryption
|
||||
let mut buf = [buf, &[0; AUTHENTICATION_TAG_SIZE_BYTES]].concat();
|
||||
|
||||
// Encrypt the file in place
|
||||
create_cipher(&config.files.encryption_key)
|
||||
.encrypt_in_place(&nonce, b"", &mut buf)
|
||||
.map_err(|_| create_error!(InternalError))?;
|
||||
|
||||
// Upload the file to remote
|
||||
report_internal_error!(
|
||||
client
|
||||
.put_object()
|
||||
.bucket(bucket_id)
|
||||
.key(path)
|
||||
.body(buf.into())
|
||||
.send()
|
||||
.await
|
||||
)?;
|
||||
|
||||
Ok(BASE64_STANDARD.encode(nonce))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user