diff --git a/src/api/core/sends.rs b/src/api/core/sends.rs index 9a7700f0..f6b8b508 100644 --- a/src/api/core/sends.rs +++ b/src/api/core/sends.rs @@ -12,7 +12,7 @@ use crate::{ api::{ApiResult, EmptyResult, JsonResult, Notify, UpdateType}, auth::{ClientIp, Headers, Host}, db::{models::*, DbConn, DbPool}, - util::{NumberOrString, SafeString}, + util::NumberOrString, CONFIG, }; @@ -346,7 +346,7 @@ async fn post_send_file_v2(data: Json, headers: Headers, mut conn: DbC #[derive(Deserialize)] #[allow(non_snake_case)] pub struct SendFileData { - id: String, + id: SendFileId, size: u64, fileName: String, } @@ -355,7 +355,7 @@ pub struct SendFileData { #[post("/sends//file/", format = "multipart/form-data", data = "")] async fn post_send_file_v2_data( uuid: SendId, - file_id: &str, + file_id: SendFileId, data: Form>, headers: Headers, mut conn: DbConn, @@ -496,7 +496,7 @@ async fn post_access( #[post("/sends//access/file/", data = "")] async fn post_access_file( uuid: SendId, - file_id: &str, + file_id: SendFileId, data: Json, host: Host, mut conn: DbConn, @@ -547,7 +547,7 @@ async fn post_access_file( ) .await; - let token_claims = crate::auth::generate_send_claims(&uuid, file_id); + let token_claims = crate::auth::generate_send_claims(&uuid, &file_id); let token = crate::auth::encode_jwt(&token_claims); Ok(Json(json!({ "object": "send-fileDownload", @@ -557,7 +557,7 @@ async fn post_access_file( } #[get("/sends//?")] -async fn download_send(uuid: SendId, file_id: SafeString, t: &str) -> Option { +async fn download_send(uuid: SendId, file_id: SendFileId, t: &str) -> Option { if let Ok(claims) = crate::auth::decode_send(t) { if claims.sub == format!("{uuid}/{file_id}") { return NamedFile::open(Path::new(&CONFIG.sends_folder()).join(uuid).join(file_id)).await.ok(); diff --git a/src/auth.rs b/src/auth.rs index 74c87a54..090aebe9 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -15,7 +15,8 @@ use std::{ }; use crate::db::models::{ - AttachmentId, CipherId, CollectionId, DeviceId, MembershipId, OrgApiKeyId, OrganizationId, SendId, UserId, + AttachmentId, CipherId, CollectionId, DeviceId, MembershipId, OrgApiKeyId, OrganizationId, SendFileId, SendId, + UserId, }; use crate::{error::Error, CONFIG}; @@ -358,7 +359,7 @@ pub fn generate_admin_claims() -> BasicJwtClaims { } } -pub fn generate_send_claims(uuid: &SendId, file_id: &str) -> BasicJwtClaims { +pub fn generate_send_claims(uuid: &SendId, file_id: &SendFileId) -> BasicJwtClaims { let time_now = Utc::now(); BasicJwtClaims { nbf: time_now.timestamp(), diff --git a/src/db/models/mod.rs b/src/db/models/mod.rs index b03b24d6..b6691e7f 100644 --- a/src/db/models/mod.rs +++ b/src/db/models/mod.rs @@ -31,7 +31,10 @@ pub use self::organization::{ Membership, MembershipId, MembershipStatus, MembershipType, OrgApiKeyId, Organization, OrganizationApiKey, OrganizationId, }; -pub use self::send::{id::SendId, Send, SendType}; +pub use self::send::{ + id::{SendFileId, SendId}, + Send, SendType, +}; pub use self::two_factor::{TwoFactor, TwoFactorType}; pub use self::two_factor_duo_context::TwoFactorDuoContext; pub use self::two_factor_incomplete::TwoFactorIncomplete; diff --git a/src/db/models/send.rs b/src/db/models/send.rs index 3ea8b660..f1ba0c0c 100644 --- a/src/db/models/send.rs +++ b/src/db/models/send.rs @@ -356,6 +356,7 @@ pub mod id { use rocket::request::FromParam; use std::marker::Send; use std::path::Path; + #[derive( Clone, Debug, AsRef, Deref, DieselNewType, Display, From, FromForm, Hash, PartialEq, Eq, Serialize, Deserialize, )] @@ -380,4 +381,27 @@ pub mod id { } } } + + #[derive(Clone, Debug, AsRef, Deref, Display, From, FromForm, Hash, PartialEq, Eq, Serialize, Deserialize)] + pub struct SendFileId(String); + + impl AsRef for SendFileId { + #[inline] + fn as_ref(&self) -> &Path { + Path::new(&self.0) + } + } + + impl<'r> FromParam<'r> for SendFileId { + type Error = (); + + #[inline(always)] + fn from_param(param: &'r str) -> Result { + if param.chars().all(|c| matches!(c, 'a'..='z' | 'A'..='Z' |'0'..='9' | '-')) { + Ok(Self(param.to_string())) + } else { + Err(()) + } + } + } }