diff --git a/apps/labrinth/src/routes/v2/version_file.rs b/apps/labrinth/src/routes/v2/version_file.rs index b9108eb1ef..3aeb40fbf9 100644 --- a/apps/labrinth/src/routes/v2/version_file.rs +++ b/apps/labrinth/src/routes/v2/version_file.rs @@ -412,9 +412,11 @@ pub struct ManyUpdateData { )] #[post("/update")] pub async fn update_files( + req: HttpRequest, pool: web::Data, redis: web::Data, update_data: web::Json, + session_queue: web::Data, ) -> Result { let update_data = update_data.into_inner(); let update_data = v3::version_file::ManyUpdateData { @@ -426,9 +428,11 @@ pub async fn update_files( }; let returned_versions = match v3::version_file::update_files( + req, pool, redis, web::Json(update_data), + session_queue, ) .await { @@ -465,9 +469,11 @@ pub async fn update_files( )] #[post("/update_many")] pub async fn update_files_many( + req: HttpRequest, pool: web::Data, redis: web::Data, update_data: web::Json, + session_queue: web::Data, ) -> Result { let update_data = update_data.into_inner(); let update_data = v3::version_file::ManyUpdateData { @@ -479,9 +485,11 @@ pub async fn update_files_many( }; let returned_versions = match v3::version_file::update_files_many( + req, pool, redis, web::Json(update_data), + session_queue, ) .await { diff --git a/apps/labrinth/src/routes/v3/version_file.rs b/apps/labrinth/src/routes/v3/version_file.rs index a773184e91..17ca2806e3 100644 --- a/apps/labrinth/src/routes/v3/version_file.rs +++ b/apps/labrinth/src/routes/v3/version_file.rs @@ -1,5 +1,7 @@ use super::ApiError; -use crate::auth::checks::{filter_visible_versions, is_visible_version}; +use crate::auth::checks::{ + filter_visible_version_ids, filter_visible_versions, is_visible_version, +}; use crate::auth::{filter_visible_projects, get_user_from_headers}; use crate::database::PgPool; use crate::database::ReadOnlyPgPool; @@ -485,21 +487,25 @@ pub struct ManyUpdateData { )] #[post("/version_files/update_many")] pub async fn update_files_many_route( + req: HttpRequest, pool: web::Data, redis: web::Data, update_data: web::Json, + session_queue: web::Data, ) -> Result>>, ApiError> { - update_files_many(pool, redis, update_data).await + update_files_many(req, pool, redis, update_data, session_queue).await } pub async fn update_files_many( + req: HttpRequest, pool: web::Data, redis: web::Data, update_data: web::Json, + session_queue: web::Data, ) -> Result>>, ApiError> { - update_files_internal(pool, redis, update_data) + update_files_internal(req, pool, redis, update_data, session_queue) .await .map(web::Json) } @@ -537,20 +543,24 @@ pub async fn update_files_many( )] #[post("/version_files/update")] pub async fn update_files_route( + req: HttpRequest, pool: web::Data, redis: web::Data, update_data: web::Json, + session_queue: web::Data, ) -> Result>, ApiError> { - update_files(pool, redis, update_data).await + update_files(req, pool, redis, update_data, session_queue).await } pub async fn update_files( + req: HttpRequest, pool: web::Data, redis: web::Data, update_data: web::Json, + session_queue: web::Data, ) -> Result>, ApiError> { let file_hashes_to_versions = - update_files_internal(pool, redis, update_data) + update_files_internal(req, pool, redis, update_data, session_queue) .await .wrap_api_err("updating files internal")?; let resp = file_hashes_to_versions @@ -564,10 +574,23 @@ pub async fn update_files( } async fn update_files_internal( + req: HttpRequest, pool: web::Data, redis: web::Data, update_data: web::Json, + session_queue: web::Data, ) -> Result>, ApiError> { + let user_option = get_user_from_headers( + &req, + &***pool, + &redis, + &session_queue, + Scopes::VERSION_READ, + ) + .await + .map(|x| x.1) + .ok(); + let algorithm = update_data .algorithm .clone() @@ -598,14 +621,12 @@ async fn update_files_internal( &update_data.game_versions.clone().unwrap_or_default(), &update_data.loaders.clone().unwrap_or_default(), &update_data.version_types.clone().unwrap_or_default().iter().map(|x| x.to_string()).collect::>(), - &*VersionStatus::iterator() - .filter(|x| !x.is_hidden()) - .map(|x| x.to_string()) - .collect::>(), - &*ProjectStatus::iterator() - .filter(|x| !x.is_hidden()) - .map(|x| x.to_string()) - .collect::>(), + &*VersionStatus::iterator() + .map(|x| x.to_string()) + .collect::>(), + &*ProjectStatus::iterator() + .map(|x| x.to_string()) + .collect::>(), ) .fetch(&***pool) .try_fold(DashMap::new(), |acc : DashMap<_,Vec>, m| { @@ -617,16 +638,38 @@ async fn update_files_internal( .await .wrap_internal_err("fetching project version IDs from database")?; + let candidate_versions = database::models::DBVersion::get_many( + &update_version_ids + .iter() + .flat_map(|x| x.value().clone()) + .collect::>(), + &***pool, + &redis, + ) + .await + .wrap_internal_err("updating versions in database")?; + let visible_version_ids = filter_visible_version_ids( + candidate_versions.iter().map(|x| &x.inner).collect(), + &user_option, + &pool, + &redis, + ) + .await + .wrap_api_err("filtering visible update versions")?; let versions = database::models::DBVersion::get_many( &update_version_ids .into_iter() - .filter_map(|x| x.1.last().copied()) + .filter_map(|x| { + x.1.into_iter() + .rev() + .find(|id| visible_version_ids.contains(id)) + }) .collect::>(), &***pool, &redis, ) .await - .wrap_internal_err("updating versions in database")?; + .wrap_internal_err("fetching latest visible update versions")?; let mut response = HashMap::>::new(); for file in files { diff --git a/apps/labrinth/tests/v2/version.rs b/apps/labrinth/tests/v2/version.rs index 6cf14aa4aa..a4d6c70f1f 100644 --- a/apps/labrinth/tests/v2/version.rs +++ b/apps/labrinth/tests/v2/version.rs @@ -16,7 +16,7 @@ use crate::common::api_v2::request_data::get_public_project_creation_data; use crate::common::dummy_data::{DummyProjectAlpha, DummyProjectBeta}; use crate::common::environment::{TestEnvironment, with_test_environment}; use crate::common::{ - database::{ENEMY_USER_PAT, USER_USER_PAT}, + database::{ADMIN_USER_PAT, ENEMY_USER_PAT, MOD_USER_PAT, USER_USER_PAT}, dummy_data::TestFile, }; @@ -141,6 +141,59 @@ pub async fn test_patch_version() { .await; } +#[actix_rt::test] +async fn update_files_respects_processing_project_visibility() { + with_test_environment( + None, + |test_env: TestEnvironment| async move { + let api = &test_env.api; + let project = &test_env.dummy.project_alpha; + + let response = api + .edit_project( + &project.project_slug, + json!({ "status": "processing" }), + ADMIN_USER_PAT, + ) + .await; + assert_status!(&response, StatusCode::NO_CONTENT); + + for pat in [USER_USER_PAT, MOD_USER_PAT] { + let versions = api + .update_files_deserialized_common( + "sha1", + vec![project.file_hash.clone()], + None, + None, + None, + pat, + ) + .await; + assert_eq!(versions.len(), 1); + assert_eq!( + versions[&project.file_hash].id.to_string(), + project.version_id + ); + } + + for pat in [ENEMY_USER_PAT, None] { + let versions = api + .update_files_deserialized_common( + "sha1", + vec![project.file_hash.clone()], + None, + None, + None, + pat, + ) + .await; + assert!(versions.is_empty()); + } + }, + ) + .await; +} + #[actix_rt::test] async fn version_updates() { // Test setup and dummy data