From b0d4bdb8ff0d9e4bb9e7c3f4132a264259e7b470 Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Tue, 17 Dec 2024 16:10:55 +0100 Subject: [PATCH] Add option to disable image upload (fixes #1118) --- crates/api_common/src/request.rs | 10 +++++----- crates/api_common/src/site.rs | 3 +++ crates/api_common/src/utils.rs | 4 ++-- crates/api_crud/src/site/read.rs | 1 + crates/routes/src/images/mod.rs | 13 +++++++++---- crates/routes/src/images/utils.rs | 4 ++-- crates/utils/src/error.rs | 1 + crates/utils/src/settings/mod.rs | 2 +- crates/utils/src/settings/structs.rs | 5 +++++ 9 files changed, 29 insertions(+), 14 deletions(-) diff --git a/crates/api_common/src/request.rs b/crates/api_common/src/request.rs index e337cbdec..a03e6598f 100644 --- a/crates/api_common/src/request.rs +++ b/crates/api_common/src/request.rs @@ -320,7 +320,7 @@ pub async fn purge_image_from_pictrs(image_url: &Url, context: &LemmyContext) -> .next_back() .ok_or(LemmyErrorType::ImageUrlMissingLastPathSegment)?; - let pictrs_config = context.settings().pictrs_config()?; + let pictrs_config = context.settings().pictrs()?; let purge_url = format!("{}internal/purge?alias={}", pictrs_config.url, alias); let pictrs_api_key = pictrs_config @@ -348,7 +348,7 @@ pub async fn delete_image_from_pictrs( delete_token: &str, context: &LemmyContext, ) -> LemmyResult<()> { - let pictrs_config = context.settings().pictrs_config()?; + let pictrs_config = context.settings().pictrs()?; let url = format!( "{}image/delete/{}/{}", pictrs_config.url, &delete_token, &alias @@ -366,7 +366,7 @@ pub async fn delete_image_from_pictrs( /// Retrieves the image with local pict-rs and generates a thumbnail. Returns the thumbnail url. #[tracing::instrument(skip_all)] async fn generate_pictrs_thumbnail(image_url: &Url, context: &LemmyContext) -> LemmyResult { - let pictrs_config = context.settings().pictrs_config()?; + let pictrs_config = context.settings().pictrs()?; match pictrs_config.image_mode() { PictrsImageMode::None => return Ok(image_url.clone()), @@ -382,7 +382,7 @@ async fn generate_pictrs_thumbnail(image_url: &Url, context: &LemmyContext) -> L "{}image/download?url={}&resize={}", pictrs_config.url, encode(image_url.as_str()), - context.settings().pictrs_config()?.max_thumbnail_size + context.settings().pictrs()?.max_thumbnail_size ); let res = context @@ -425,7 +425,7 @@ pub async fn fetch_pictrs_proxied_image_details( image_url: &Url, context: &LemmyContext, ) -> LemmyResult { - let pictrs_url = context.settings().pictrs_config()?.url; + let pictrs_url = context.settings().pictrs()?.url; let encoded_image_url = encode(image_url.as_str()); // Pictrs needs you to fetch the proxied image before you can fetch the details diff --git a/crates/api_common/src/site.rs b/crates/api_common/src/site.rs index 7f1000b14..0b1fb2200 100644 --- a/crates/api_common/src/site.rs +++ b/crates/api_common/src/site.rs @@ -455,6 +455,9 @@ pub struct GetSiteResponse { #[cfg_attr(feature = "full", ts(optional))] pub admin_oauth_providers: Option>, pub blocked_urls: Vec, + // If true then uploads for post images or markdown images are disabled. Only avatars, icons and + // banners can be set. + pub image_upload_disabled: bool, } #[skip_serializing_none] diff --git a/crates/api_common/src/utils.rs b/crates/api_common/src/utils.rs index 6daeec4cd..3daa34107 100644 --- a/crates/api_common/src/utils.rs +++ b/crates/api_common/src/utils.rs @@ -1060,7 +1060,7 @@ pub async fn process_markdown( markdown_check_for_blocked_urls(&text, url_blocklist)?; - if context.settings().pictrs_config()?.image_mode() == PictrsImageMode::ProxyAllImages { + if context.settings().pictrs()?.image_mode() == PictrsImageMode::ProxyAllImages { let (text, links) = markdown_rewrite_image_links(text); RemoteImage::create(&mut context.pool(), links.clone()).await?; @@ -1130,7 +1130,7 @@ async fn proxy_image_link_internal( pub async fn proxy_image_link(link: Url, context: &LemmyContext) -> LemmyResult { proxy_image_link_internal( link, - context.settings().pictrs_config()?.image_mode(), + context.settings().pictrs()?.image_mode(), context, ) .await diff --git a/crates/api_crud/src/site/read.rs b/crates/api_crud/src/site/read.rs index 220fe1bd5..b618c0fa2 100644 --- a/crates/api_crud/src/site/read.rs +++ b/crates/api_crud/src/site/read.rs @@ -69,5 +69,6 @@ async fn read_site(context: &LemmyContext) -> LemmyResult { tagline, oauth_providers: Some(oauth_providers), admin_oauth_providers: Some(admin_oauth_providers), + image_upload_disabled: context.settings().pictrs()?.disable_image_upload, }) } diff --git a/crates/routes/src/images/mod.rs b/crates/routes/src/images/mod.rs index 632aa30f2..8e5a54734 100644 --- a/crates/routes/src/images/mod.rs +++ b/crates/routes/src/images/mod.rs @@ -2,6 +2,7 @@ use actix_web::{body::BoxBody, web::*, HttpRequest, HttpResponse, Responder}; use lemmy_api_common::{ context::LemmyContext, image::{DeleteImageParams, ImageGetParams, ImageProxyParams, UploadImageResponse}, + LemmyErrorType, SuccessResponse, }; use lemmy_db_schema::source::{ @@ -22,6 +23,10 @@ pub async fn upload_image( local_user_view: LocalUserView, context: Data, ) -> LemmyResult> { + if context.settings().pictrs()?.disable_image_upload { + return Err(LemmyErrorType::ImageUploadDisabled.into()); + } + let image = do_upload_image(req, body, UploadType::Other, &local_user_view, &context).await?; let image_url = image.image_url(&context.settings().get_protocol_and_hostname())?; @@ -47,7 +52,7 @@ pub async fn get_image( let name = &filename.into_inner(); // If there are no query params, the URL is original - let pictrs_url = context.settings().pictrs_config()?.url; + let pictrs_url = context.settings().pictrs()?.url; let processed_url = if params.file_type.is_none() && params.max_size.is_none() { format!("{}image/original/{}", pictrs_url, name) } else { @@ -69,7 +74,7 @@ pub async fn delete_image( // require login _local_user_view: LocalUserView, ) -> LemmyResult> { - let pictrs_config = context.settings().pictrs_config()?; + let pictrs_config = context.settings().pictrs()?; let url = format!( "{}image/delete/{}/{}", pictrs_config.url, &data.token, &data.filename @@ -83,7 +88,7 @@ pub async fn delete_image( } pub async fn pictrs_health(context: Data) -> LemmyResult> { - let pictrs_config = context.settings().pictrs_config()?; + let pictrs_config = context.settings().pictrs()?; let url = format!("{}healthz", pictrs_config.url); PICTRS_CLIENT.get(url).send().await?.error_for_status()?; @@ -102,7 +107,7 @@ pub async fn image_proxy( // for arbitrary purposes. RemoteImage::validate(&mut context.pool(), url.clone().into()).await?; - let pictrs_config = context.settings().pictrs_config()?; + let pictrs_config = context.settings().pictrs()?; let processed_url = if params.file_type.is_none() && params.max_size.is_none() { format!("{}image/original?proxy={}", pictrs_config.url, params.url) } else { diff --git a/crates/routes/src/images/utils.rs b/crates/routes/src/images/utils.rs index e4b243201..50e0b8fd4 100644 --- a/crates/routes/src/images/utils.rs +++ b/crates/routes/src/images/utils.rs @@ -125,7 +125,7 @@ pub(super) async fn do_upload_image( local_user_view: &LocalUserView, context: &Data, ) -> LemmyResult { - let pictrs_config = context.settings().pictrs_config()?; + let pictrs_config = context.settings().pictrs()?; let image_url = format!("{}image", pictrs_config.url); let mut client_req = adapt_request(&req, image_url); @@ -134,7 +134,7 @@ pub(super) async fn do_upload_image( UploadType::Avatar => { let max_size = context .settings() - .pictrs_config()? + .pictrs()? .max_avatar_size .to_string(); client_req.query(&[ diff --git a/crates/utils/src/error.rs b/crates/utils/src/error.rs index d2605608e..d2a40b4ec 100644 --- a/crates/utils/src/error.rs +++ b/crates/utils/src/error.rs @@ -31,6 +31,7 @@ pub enum LemmyErrorType { NoContentTypeHeader, NotAnImageType, InvalidImageUpload, + ImageUploadDisabled, NotAModOrAdmin, NotTopMod, NotLoggedIn, diff --git a/crates/utils/src/settings/mod.rs b/crates/utils/src/settings/mod.rs index 72d986d2d..6ef535ab9 100644 --- a/crates/utils/src/settings/mod.rs +++ b/crates/utils/src/settings/mod.rs @@ -97,7 +97,7 @@ impl Settings { WEBFINGER_REGEX.clone() } - pub fn pictrs_config(&self) -> LemmyResult { + pub fn pictrs(&self) -> LemmyResult { self .pictrs .clone() diff --git a/crates/utils/src/settings/structs.rs b/crates/utils/src/settings/structs.rs index 28710e8ca..041980d2e 100644 --- a/crates/utils/src/settings/structs.rs +++ b/crates/utils/src/settings/structs.rs @@ -116,6 +116,11 @@ pub struct PictrsConfig { /// if image is larger. #[default(512)] pub max_banner_size: u32, + + /// Prevent users from uploading images for posts or embedding in markdown. Avatars, icons and + /// banners can still be uploaded. + #[default(false)] + pub disable_image_upload: bool, } #[derive(Debug, Deserialize, Serialize, Clone, SmartDefault, Document, PartialEq)]