diff --git a/.sqlx/query-1997511538affe27f229df10ddc747c60aca09862d39590431ac12f6ad585aab.json b/.sqlx/query-1997511538affe27f229df10ddc747c60aca09862d39590431ac12f6ad585aab.json deleted file mode 100644 index 635fbd40..00000000 --- a/.sqlx/query-1997511538affe27f229df10ddc747c60aca09862d39590431ac12f6ad585aab.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT EXISTS(\n SELECT 1 FROM mods m \n INNER JOIN organizations o ON m.organization_id = o.id\n INNER JOIN team_members tm ON tm.team_id = o.team_id AND user_id = $2 \n WHERE m.id = $1\n )", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "exists", - "type_info": "Bool" - } - ], - "parameters": { - "Left": [ - "Int8", - "Int8" - ] - }, - "nullable": [ - null - ] - }, - "hash": "1997511538affe27f229df10ddc747c60aca09862d39590431ac12f6ad585aab" -} diff --git a/.sqlx/query-23829a50afd2a4fd1b2d1770d2f854abf0f67f225622935802379de495dce18b.json b/.sqlx/query-23829a50afd2a4fd1b2d1770d2f854abf0f67f225622935802379de495dce18b.json deleted file mode 100644 index 8f77eadb..00000000 --- a/.sqlx/query-23829a50afd2a4fd1b2d1770d2f854abf0f67f225622935802379de495dce18b.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT m.id id, m.team_id team_id FROM team_members tm\n INNER JOIN mods m ON m.team_id = tm.team_id\n WHERE tm.team_id = ANY($1) AND tm.user_id = $3\n\n UNION\n\n SELECT m.id id, m.team_id team_id FROM team_members tm\n INNER JOIN organizations o ON o.team_id = tm.team_id\n INNER JOIN mods m ON m.organization_id = o.id\n WHERE o.id = ANY($2) AND tm.user_id = $3\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Int8" - }, - { - "ordinal": 1, - "name": "team_id", - "type_info": "Int8" - } - ], - "parameters": { - "Left": [ - "Int8Array", - "Int8Array", - "Int8" - ] - }, - "nullable": [ - null, - null - ] - }, - "hash": "23829a50afd2a4fd1b2d1770d2f854abf0f67f225622935802379de495dce18b" -} diff --git a/.sqlx/query-5627b3516fc7c3799154098a663b1586aac11b2dc736810f06630ee5d8a54946.json b/.sqlx/query-5627b3516fc7c3799154098a663b1586aac11b2dc736810f06630ee5d8a54946.json deleted file mode 100644 index b5d3a456..00000000 --- a/.sqlx/query-5627b3516fc7c3799154098a663b1586aac11b2dc736810f06630ee5d8a54946.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT c.id id, c.user_id user_id FROM collections c\n WHERE c.user_id = $2 AND c.id = ANY($1)\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Int8" - }, - { - "ordinal": 1, - "name": "user_id", - "type_info": "Int8" - } - ], - "parameters": { - "Left": [ - "Int8Array", - "Int8" - ] - }, - "nullable": [ - false, - false - ] - }, - "hash": "5627b3516fc7c3799154098a663b1586aac11b2dc736810f06630ee5d8a54946" -} diff --git a/.sqlx/query-5942afe6eef37e3833a9a25f943a864d9eff046fcb74780fb49ffda96eabc2a9.json b/.sqlx/query-5942afe6eef37e3833a9a25f943a864d9eff046fcb74780fb49ffda96eabc2a9.json new file mode 100644 index 00000000..a52939d1 --- /dev/null +++ b/.sqlx/query-5942afe6eef37e3833a9a25f943a864d9eff046fcb74780fb49ffda96eabc2a9.json @@ -0,0 +1,30 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT m.id id, m.team_id team_id FROM team_members tm\n INNER JOIN mods m ON m.team_id = tm.team_id\n LEFT JOIN organizations o ON o.team_id = tm.team_id\n WHERE tm.team_id = ANY($1) AND tm.user_id = $3\n UNION\n SELECT m.id id, m.team_id team_id FROM team_members tm\n INNER JOIN organizations o ON o.team_id = tm.team_id\n INNER JOIN mods m ON m.organization_id = o.id\n WHERE o.id = ANY($2) AND tm.user_id = $3\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "team_id", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Int8Array", + "Int8Array", + "Int8" + ] + }, + "nullable": [ + null, + null + ] + }, + "hash": "5942afe6eef37e3833a9a25f943a864d9eff046fcb74780fb49ffda96eabc2a9" +} diff --git a/.sqlx/query-796f057ea8eb5b01d3eedeee9840fb37464ea567f32871953fb07e14ed86af1c.json b/.sqlx/query-796f057ea8eb5b01d3eedeee9840fb37464ea567f32871953fb07e14ed86af1c.json deleted file mode 100644 index af6d19f0..00000000 --- a/.sqlx/query-796f057ea8eb5b01d3eedeee9840fb37464ea567f32871953fb07e14ed86af1c.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT EXISTS(SELECT 1 FROM team_members WHERE team_id = $1 AND user_id = $2)", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "exists", - "type_info": "Bool" - } - ], - "parameters": { - "Left": [ - "Int8", - "Int8" - ] - }, - "nullable": [ - null - ] - }, - "hash": "796f057ea8eb5b01d3eedeee9840fb37464ea567f32871953fb07e14ed86af1c" -} diff --git a/.sqlx/query-7f039929345f6421ab7de7a75ac19cf7caf9a0ebc53b0232c6107b2995453c14.json b/.sqlx/query-7f039929345f6421ab7de7a75ac19cf7caf9a0ebc53b0232c6107b2995453c14.json deleted file mode 100644 index 3dad0e1c..00000000 --- a/.sqlx/query-7f039929345f6421ab7de7a75ac19cf7caf9a0ebc53b0232c6107b2995453c14.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT EXISTS(\n SELECT 1 FROM mods m \n INNER JOIN team_members tm ON tm.team_id = m.team_id AND user_id = $2 \n WHERE m.id = $1\n )", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "exists", - "type_info": "Bool" - } - ], - "parameters": { - "Left": [ - "Int8", - "Int8" - ] - }, - "nullable": [ - null - ] - }, - "hash": "7f039929345f6421ab7de7a75ac19cf7caf9a0ebc53b0232c6107b2995453c14" -} diff --git a/.sqlx/query-b139baf2b1424d1f38b9d80f3a33baf12195bcbac34bb779483e42315803b875.json b/.sqlx/query-b139baf2b1424d1f38b9d80f3a33baf12195bcbac34bb779483e42315803b875.json deleted file mode 100644 index 6005cda3..00000000 --- a/.sqlx/query-b139baf2b1424d1f38b9d80f3a33baf12195bcbac34bb779483e42315803b875.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT EXISTS(\n SELECT 1 \n FROM organizations o JOIN team_members tm ON tm.team_id = o.team_id\n WHERE o.id = $1 AND tm.user_id = $2\n )", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "exists", - "type_info": "Bool" - } - ], - "parameters": { - "Left": [ - "Int8", - "Int8" - ] - }, - "nullable": [ - null - ] - }, - "hash": "b139baf2b1424d1f38b9d80f3a33baf12195bcbac34bb779483e42315803b875" -} diff --git a/src/auth/checks.rs b/src/auth/checks.rs index e066bb40..fdf173f8 100644 --- a/src/auth/checks.rs +++ b/src/auth/checks.rs @@ -7,6 +7,7 @@ use crate::database::{models, Project, Version}; use crate::models::users::User; use crate::routes::ApiError; use actix_web::web; +use itertools::Itertools; use sqlx::PgPool; pub trait ValidateAuthorized { @@ -28,179 +29,168 @@ where } } -pub async fn is_authorized( +pub async fn is_visible_project( project_data: &Project, user_option: &Option, pool: &web::Data, ) -> Result { - let mut authorized = !project_data.status.is_hidden(); - - if let Some(user) = &user_option { - if !authorized { - if user.role.is_mod() { - authorized = true; - } else { - let user_id: models::ids::UserId = user.id.into(); - - let project_exists = sqlx::query!( - "SELECT EXISTS(SELECT 1 FROM team_members WHERE team_id = $1 AND user_id = $2)", - project_data.team_id as database::models::ids::TeamId, - user_id as database::models::ids::UserId, - ) - .fetch_one(&***pool) - .await? - .exists; - - let organization_exists = - if let Some(organization_id) = project_data.organization_id { - sqlx::query!( - "SELECT EXISTS( - SELECT 1 - FROM organizations o JOIN team_members tm ON tm.team_id = o.team_id - WHERE o.id = $1 AND tm.user_id = $2 - )", - organization_id as database::models::ids::OrganizationId, - user_id as database::models::ids::UserId, - ) - .fetch_one(&***pool) - .await? - .exists - } else { - None - }; - - authorized = - project_exists.unwrap_or(false) || organization_exists.unwrap_or(false); - } - } - } + filter_visible_project_ids(vec![project_data], user_option, pool) + .await + .map(|x| !x.is_empty()) +} - Ok(authorized) +pub async fn is_team_member_project( + project_data: &Project, + user_option: &Option, + pool: &web::Data, +) -> Result { + filter_enlisted_projects_ids(vec![project_data], user_option, pool) + .await + .map(|x| !x.is_empty()) } -pub async fn filter_authorized_projects( - projects: Vec, +pub async fn filter_visible_projects( + mut projects: Vec, user_option: &Option, pool: &web::Data, ) -> Result, ApiError> { + let filtered_project_ids = filter_visible_project_ids( + projects.iter().map(|x| &x.inner).collect_vec(), + user_option, + pool, + ) + .await + .unwrap(); + projects.retain(|x| filtered_project_ids.contains(&x.inner.id)); + Ok(projects.into_iter().map(|x| x.into()).collect()) +} + +// Filters projects for which we can see, meaning one of the following is true: +// - it's not hidden +// - the user is enlisted on the project's team (filter_enlisted_projects) +// - the user is a mod +// This is essentially whether you can know of the project's existence +pub async fn filter_visible_project_ids( + projects: Vec<&Project>, + user_option: &Option, + pool: &web::Data, +) -> Result, ApiError> { let mut return_projects = Vec::new(); let mut check_projects = Vec::new(); + // Return projects that are not hidden or we are a mod of for project in projects { - if !project.inner.status.is_hidden() + if !project.status.is_hidden() || user_option .as_ref() .map(|x| x.role.is_mod()) .unwrap_or(false) { - return_projects.push(project.into()); + return_projects.push(project.id); } else if user_option.is_some() { check_projects.push(project); } } + // For hidden projects, return a filtered list of projects for which we are enlisted on the team if !check_projects.is_empty() { - if let Some(user) = user_option { - let user_id: models::ids::UserId = user.id.into(); - - use futures::TryStreamExt; - - sqlx::query!( - " - SELECT m.id id, m.team_id team_id FROM team_members tm - INNER JOIN mods m ON m.team_id = tm.team_id - WHERE tm.team_id = ANY($1) AND tm.user_id = $3 - - UNION - - SELECT m.id id, m.team_id team_id FROM team_members tm - INNER JOIN organizations o ON o.team_id = tm.team_id - INNER JOIN mods m ON m.organization_id = o.id - WHERE o.id = ANY($2) AND tm.user_id = $3 - ", - &check_projects - .iter() - .map(|x| x.inner.team_id.0) - .collect::>(), - &check_projects - .iter() - .filter_map(|x| x.inner.organization_id.map(|x| x.0)) - .collect::>(), - user_id as database::models::ids::UserId, - ) - .fetch_many(&***pool) - .try_for_each(|e| { - if let Some(row) = e.right() { - check_projects.retain(|x| { - let bool = - Some(x.inner.id.0) == row.id && Some(x.inner.team_id.0) == row.team_id; - - if bool { - return_projects.push(x.clone().into()); - } - - !bool - }); + return_projects + .extend(filter_enlisted_projects_ids(check_projects, user_option, pool).await?); + } + + Ok(return_projects) +} + +// Filters out projects for which we are a member of the team (or a mod) +// These are projects we have internal access to and can potentially see even if they are hidden +// This is useful for getting visibility of versions, or seeing analytics or sensitive team-restricted data of a project +pub async fn filter_enlisted_projects_ids( + projects: Vec<&Project>, + user_option: &Option, + pool: &web::Data, +) -> Result, ApiError> { + let mut return_projects = vec![]; + + if let Some(user) = user_option { + let user_id: models::ids::UserId = user.id.into(); + + use futures::TryStreamExt; + + sqlx::query!( + " + SELECT m.id id, m.team_id team_id FROM team_members tm + INNER JOIN mods m ON m.team_id = tm.team_id + LEFT JOIN organizations o ON o.team_id = tm.team_id + WHERE tm.team_id = ANY($1) AND tm.user_id = $3 + UNION + SELECT m.id id, m.team_id team_id FROM team_members tm + INNER JOIN organizations o ON o.team_id = tm.team_id + INNER JOIN mods m ON m.organization_id = o.id + WHERE o.id = ANY($2) AND tm.user_id = $3 + ", + &projects.iter().map(|x| x.team_id.0).collect::>(), + &projects + .iter() + .filter_map(|x| x.organization_id.map(|x| x.0)) + .collect::>(), + user_id as database::models::ids::UserId, + ) + .fetch_many(&***pool) + .try_for_each(|e| { + if let Some(row) = e.right() { + for x in projects.iter() { + let bool = Some(x.id.0) == row.id && Some(x.team_id.0) == row.team_id; + if bool { + return_projects.push(x.id); + } } + } - futures::future::ready(Ok(())) - }) - .await?; - } + futures::future::ready(Ok(())) + }) + .await?; } - Ok(return_projects) } -pub async fn is_authorized_version( +pub async fn is_visible_version( version_data: &Version, user_option: &Option, pool: &web::Data, + redis: &RedisPool, ) -> Result { - let mut authorized = !version_data.status.is_hidden(); + filter_visible_version_ids(vec![version_data], user_option, pool, redis) + .await + .map(|x| !x.is_empty()) +} - if let Some(user) = &user_option { - if !authorized { - if user.role.is_mod() { - authorized = true; - } else { - let user_id: models::ids::UserId = user.id.into(); - - let version_exists = sqlx::query!( - "SELECT EXISTS( - SELECT 1 FROM mods m - INNER JOIN team_members tm ON tm.team_id = m.team_id AND user_id = $2 - WHERE m.id = $1 - )", - version_data.project_id as database::models::ids::ProjectId, - user_id as database::models::ids::UserId, - ) - .fetch_one(&***pool) - .await? - .exists; - - let version_organization_exists = sqlx::query!( - "SELECT EXISTS( - SELECT 1 FROM mods m - INNER JOIN organizations o ON m.organization_id = o.id - INNER JOIN team_members tm ON tm.team_id = o.team_id AND user_id = $2 - WHERE m.id = $1 - )", - version_data.project_id as database::models::ids::ProjectId, - user_id as database::models::ids::UserId, - ) - .fetch_one(&***pool) - .await? - .exists; - - authorized = version_exists - .or(version_organization_exists) - .unwrap_or(false); - } - } - } +pub async fn is_team_member_version( + version_data: &Version, + user_option: &Option, + pool: &web::Data, + redis: &RedisPool, +) -> Result { + filter_enlisted_version_ids(vec![version_data], user_option, pool, redis) + .await + .map(|x| !x.is_empty()) +} - Ok(authorized) +pub async fn filter_visible_versions( + mut versions: Vec, + user_option: &Option, + pool: &web::Data, + redis: &RedisPool, +) -> Result, ApiError> { + let filtered_version_ids = filter_visible_version_ids( + versions.iter().map(|x| &x.inner).collect_vec(), + user_option, + pool, + redis, + ) + .await + .unwrap(); + versions.retain(|x| filtered_version_ids.contains(&x.inner.id)); + Ok(versions.into_iter().map(|x| x.into()).collect()) } impl ValidateAuthorized for models::OAuthClient { @@ -220,62 +210,111 @@ impl ValidateAuthorized for models::OAuthClient { } } -pub async fn filter_authorized_versions( - versions: Vec, +pub async fn filter_visible_version_ids( + versions: Vec<&Version>, user_option: &Option, pool: &web::Data, - redis: web::Data, -) -> Result, ApiError> { + redis: &RedisPool, +) -> Result, ApiError> { let mut return_versions = Vec::new(); - - let project_ids = versions - .iter() - .map(|x| x.inner.project_id) - .collect::>(); - - let authorized_projects = filter_authorized_projects( - Project::get_many_ids(&project_ids, &***pool, &redis).await?, + let mut check_versions = Vec::new(); + + // First, filter out versions belonging to projects we can't see + // (ie: a hidden project, but public version, should still be hidden) + // Gets project ids of versions + let project_ids = versions.iter().map(|x| x.project_id).collect::>(); + + // Get visible projects- ones we are allowed to see public versions for. + let visible_project_ids = filter_visible_project_ids( + Project::get_many_ids(&project_ids, &***pool, redis) + .await? + .iter() + .map(|x| &x.inner) + .collect(), user_option, pool, ) .await?; - let authorized_project_ids: Vec<_> = authorized_projects.iter().map(|x| x.id.into()).collect(); + // Then, get enlisted versions (Versions that are a part of a project we are a member of) + let enlisted_version_ids = + filter_enlisted_version_ids(versions.clone(), user_option, pool, redis).await?; + // Return versions that are not hidden, we are a mod of, or we are enlisted on the team of for version in versions { - if !version.inner.status.is_hidden() + // We can see the version if: + // - it's not hidden and we can see the project + // - we are a mod + // - we are enlisted on the team of the mod + if (!version.status.is_hidden() && visible_project_ids.contains(&version.project_id)) || user_option .as_ref() .map(|x| x.role.is_mod()) .unwrap_or(false) - || (user_option.is_some() && authorized_project_ids.contains(&version.inner.project_id)) + || enlisted_version_ids.contains(&version.id) + { + return_versions.push(version.id); + } else if user_option.is_some() { + check_versions.push(version); + } + } + + Ok(return_versions) +} + +pub async fn filter_enlisted_version_ids( + versions: Vec<&Version>, + user_option: &Option, + pool: &web::Data, + redis: &RedisPool, +) -> Result, ApiError> { + let mut return_versions = Vec::new(); + + // Get project ids of versions + let project_ids = versions.iter().map(|x| x.project_id).collect::>(); + + // Get enlisted projects- ones we are allowed to see hidden versions for. + let authorized_project_ids = filter_enlisted_projects_ids( + Project::get_many_ids(&project_ids, &***pool, redis) + .await? + .iter() + .map(|x| &x.inner) + .collect(), + user_option, + pool, + ) + .await?; + + for version in versions { + if user_option + .as_ref() + .map(|x| x.role.is_mod()) + .unwrap_or(false) + || (user_option.is_some() && authorized_project_ids.contains(&version.project_id)) { - return_versions.push(version.into()); + return_versions.push(version.id); } } Ok(return_versions) } -pub async fn is_authorized_collection( +pub async fn is_visible_collection( collection_data: &Collection, user_option: &Option, ) -> Result { let mut authorized = !collection_data.status.is_hidden(); - if let Some(user) = &user_option { if !authorized && (user.role.is_mod() || user.id == collection_data.user_id.into()) { authorized = true; } } - Ok(authorized) } -pub async fn filter_authorized_collections( +pub async fn filter_visible_collections( collections: Vec, user_option: &Option, - pool: &web::Data, ) -> Result, ApiError> { let mut return_collections = Vec::new(); let mut check_collections = Vec::new(); @@ -293,37 +332,12 @@ pub async fn filter_authorized_collections( } } - if !check_collections.is_empty() { + for collection in check_collections { + // Collections are simple- if we are the owner or a mod, we can see it if let Some(user) = user_option { - let user_id: models::ids::UserId = user.id.into(); - - use futures::TryStreamExt; - - sqlx::query!( - " - SELECT c.id id, c.user_id user_id FROM collections c - WHERE c.user_id = $2 AND c.id = ANY($1) - ", - &check_collections.iter().map(|x| x.id.0).collect::>(), - user_id as database::models::ids::UserId, - ) - .fetch_many(&***pool) - .try_for_each(|e| { - if let Some(row) = e.right() { - check_collections.retain(|x| { - let bool = x.id.0 == row.id && x.user_id.0 == row.user_id; - - if bool { - return_collections.push(x.clone().into()); - } - - !bool - }); - } - - futures::future::ready(Ok(())) - }) - .await?; + if user.role.is_mod() || user.id == collection.user_id.into() { + return_collections.push(collection.into()); + } } } diff --git a/src/auth/mod.rs b/src/auth/mod.rs index 4a8c8f6b..305743c3 100644 --- a/src/auth/mod.rs +++ b/src/auth/mod.rs @@ -4,7 +4,8 @@ pub mod oauth; pub mod templates; pub mod validate; pub use checks::{ - filter_authorized_projects, filter_authorized_versions, is_authorized, is_authorized_version, + filter_enlisted_projects_ids, filter_enlisted_version_ids, filter_visible_collections, + filter_visible_project_ids, filter_visible_projects, }; use serde::{Deserialize, Serialize}; // pub use pat::{generate_pat, PersonalAccessToken}; diff --git a/src/routes/maven.rs b/src/routes/maven.rs index cb8fd45f..aeb9bb88 100644 --- a/src/routes/maven.rs +++ b/src/routes/maven.rs @@ -1,3 +1,4 @@ +use crate::auth::checks::{is_visible_project, is_visible_version}; use crate::database::models::legacy_loader_fields::MinecraftGameVersion; use crate::database::models::loader_fields::Loader; use crate::database::models::project_item::QueryProject; @@ -7,10 +8,7 @@ use crate::models::pats::Scopes; use crate::models::projects::{ProjectId, VersionId}; use crate::queue::session::AuthQueue; use crate::routes::ApiError; -use crate::{ - auth::{get_user_from_headers, is_authorized, is_authorized_version}, - database, -}; +use crate::{auth::get_user_from_headers, database}; use actix_web::{get, route, web, HttpRequest, HttpResponse}; use sqlx::PgPool; use std::collections::HashSet; @@ -94,7 +92,7 @@ pub async fn maven_metadata( .map(|x| x.1) .ok(); - if !is_authorized(&project.inner, &user_option, &pool).await? { + if !is_visible_project(&project.inner, &user_option, &pool).await? { return Err(ApiError::NotFound); } @@ -288,7 +286,7 @@ pub async fn version_file( .map(|x| x.1) .ok(); - if !is_authorized(&project.inner, &user_option, &pool).await? { + if !is_visible_project(&project.inner, &user_option, &pool).await? { return Err(ApiError::NotFound); } @@ -296,7 +294,7 @@ pub async fn version_file( return Err(ApiError::NotFound); }; - if !is_authorized_version(&version.inner, &user_option, &pool).await? { + if !is_visible_version(&version.inner, &user_option, &pool, &redis).await? { return Err(ApiError::NotFound); } @@ -349,7 +347,7 @@ pub async fn version_file_sha1( .map(|x| x.1) .ok(); - if !is_authorized(&project.inner, &user_option, &pool).await? { + if !is_visible_project(&project.inner, &user_option, &pool).await? { return Err(ApiError::NotFound); } @@ -357,7 +355,7 @@ pub async fn version_file_sha1( return Err(ApiError::NotFound); }; - if !is_authorized_version(&version.inner, &user_option, &pool).await? { + if !is_visible_version(&version.inner, &user_option, &pool, &redis).await? { return Err(ApiError::NotFound); } @@ -391,7 +389,7 @@ pub async fn version_file_sha512( .map(|x| x.1) .ok(); - if !is_authorized(&project.inner, &user_option, &pool).await? { + if !is_visible_project(&project.inner, &user_option, &pool).await? { return Err(ApiError::NotFound); } @@ -399,7 +397,7 @@ pub async fn version_file_sha512( return Err(ApiError::NotFound); }; - if !is_authorized_version(&version.inner, &user_option, &pool).await? { + if !is_visible_version(&version.inner, &user_option, &pool, &redis).await? { return Err(ApiError::NotFound); } diff --git a/src/routes/updates.rs b/src/routes/updates.rs index 2d29af8b..f5688a91 100644 --- a/src/routes/updates.rs +++ b/src/routes/updates.rs @@ -4,7 +4,8 @@ use actix_web::{get, web, HttpRequest, HttpResponse}; use serde::{Deserialize, Serialize}; use sqlx::PgPool; -use crate::auth::{filter_authorized_versions, get_user_from_headers, is_authorized}; +use crate::auth::checks::{filter_visible_versions, is_visible_project}; +use crate::auth::get_user_from_headers; use crate::database; use crate::database::models::legacy_loader_fields::MinecraftGameVersion; use crate::database::redis::RedisPool; @@ -56,7 +57,7 @@ pub async fn forge_updates( .map(|x| x.1) .ok(); - if !is_authorized(&project.inner, &user_option, &pool).await? { + if !is_visible_project(&project.inner, &user_option, &pool).await? { return Err(ApiError::InvalidInput(ERROR.to_string())); } @@ -68,14 +69,14 @@ pub async fn forge_updates( _ => |x: &String| *x == "forge", }; - let mut versions = filter_authorized_versions( + let mut versions = filter_visible_versions( versions .into_iter() .filter(|x| x.loaders.iter().any(loaders)) .collect(), &user_option, &pool, - redis, + &redis, ) .await?; diff --git a/src/routes/v3/collections.rs b/src/routes/v3/collections.rs index 7eff98c5..51425a60 100644 --- a/src/routes/v3/collections.rs +++ b/src/routes/v3/collections.rs @@ -1,5 +1,5 @@ -use crate::auth::checks::{filter_authorized_collections, is_authorized_collection}; -use crate::auth::get_user_from_headers; +use crate::auth::checks::is_visible_collection; +use crate::auth::{filter_visible_collections, get_user_from_headers}; use crate::database::models::{collection_item, generate_collection_id, project_item}; use crate::database::redis::RedisPool; use crate::file_hosting::FileHost; @@ -155,7 +155,7 @@ pub async fn collections_get( .map(|x| x.1) .ok(); - let collections = filter_authorized_collections(collections_data, &user_option, &pool).await?; + let collections = filter_visible_collections(collections_data, &user_option).await?; Ok(HttpResponse::Ok().json(collections)) } @@ -183,7 +183,7 @@ pub async fn collection_get( .ok(); if let Some(data) = collection_data { - if is_authorized_collection(&data, &user_option).await? { + if is_visible_collection(&data, &user_option).await? { return Ok(HttpResponse::Ok().json(Collection::from(data))); } } diff --git a/src/routes/v3/images.rs b/src/routes/v3/images.rs index e287426a..86b202ef 100644 --- a/src/routes/v3/images.rs +++ b/src/routes/v3/images.rs @@ -1,6 +1,7 @@ use std::sync::Arc; -use crate::auth::{get_user_from_headers, is_authorized, is_authorized_version}; +use crate::auth::checks::{is_team_member_project, is_team_member_version}; +use crate::auth::get_user_from_headers; use crate::database; use crate::database::models::{project_item, report_item, thread_item, version_item}; use crate::database::redis::RedisPool; @@ -62,7 +63,9 @@ pub async fn images_add( if let Some(id) = data.project_id { let project = project_item::Project::get(&id, &**pool, &redis).await?; if let Some(project) = project { - if is_authorized(&project.inner, &Some(user.clone()), &pool).await? { + if is_team_member_project(&project.inner, &Some(user.clone()), &pool) + .await? + { *project_id = Some(project.inner.id.into()); } else { return Err(ApiError::CustomAuthentication( @@ -81,7 +84,13 @@ pub async fn images_add( if let Some(id) = data.version_id { let version = version_item::Version::get(id.into(), &**pool, &redis).await?; if let Some(version) = version { - if is_authorized_version(&version.inner, &Some(user.clone()), &pool).await? + if is_team_member_version( + &version.inner, + &Some(user.clone()), + &pool, + &redis, + ) + .await? { *version_id = Some(version.inner.id.into()); } else { diff --git a/src/routes/v3/organizations.rs b/src/routes/v3/organizations.rs index a49217a9..75c14cc3 100644 --- a/src/routes/v3/organizations.rs +++ b/src/routes/v3/organizations.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use std::sync::Arc; use super::ApiError; -use crate::auth::{filter_authorized_projects, get_user_from_headers}; +use crate::auth::{filter_visible_projects, get_user_from_headers}; use crate::database::models::team_item::TeamMember; use crate::database::models::{generate_organization_id, team_item, Organization}; use crate::database::redis::RedisPool; @@ -85,7 +85,7 @@ pub async fn organization_projects_get( let projects_data = crate::database::models::Project::get_many_ids(&project_ids, &**pool, &redis).await?; - let projects = filter_authorized_projects(projects_data, ¤t_user, &pool).await?; + let projects = filter_visible_projects(projects_data, ¤t_user, &pool).await?; Ok(HttpResponse::Ok().json(projects)) } diff --git a/src/routes/v3/projects.rs b/src/routes/v3/projects.rs index fb9b7188..0d37dc45 100644 --- a/src/routes/v3/projects.rs +++ b/src/routes/v3/projects.rs @@ -1,7 +1,8 @@ use std::collections::HashMap; use std::sync::Arc; -use crate::auth::{filter_authorized_projects, get_user_from_headers, is_authorized}; +use crate::auth::checks::is_visible_project; +use crate::auth::{filter_visible_projects, get_user_from_headers}; use crate::database::models::notification_item::NotificationBuilder; use crate::database::models::project_item::{GalleryItem, ModCategory}; use crate::database::models::thread_item::ThreadMessageBuilder; @@ -136,7 +137,7 @@ pub async fn projects_get( .map(|x| x.1) .ok(); - let projects = filter_authorized_projects(projects_data, &user_option, &pool).await?; + let projects = filter_visible_projects(projects_data, &user_option, &pool).await?; Ok(HttpResponse::Ok().json(projects)) } @@ -163,7 +164,7 @@ pub async fn project_get( .ok(); if let Some(data) = project_data { - if is_authorized(&data.inner, &user_option, &pool).await? { + if is_visible_project(&data.inner, &user_option, &pool).await? { return Ok(HttpResponse::Ok().json(Project::from(data))); } } @@ -968,7 +969,7 @@ pub async fn dependency_list( .ok(); if let Some(project) = result { - if !is_authorized(&project.inner, &user_option, &pool).await? { + if !is_visible_project(&project.inner, &user_option, &pool).await? { return Err(ApiError::NotFound); } @@ -2061,7 +2062,7 @@ pub async fn project_follow( let user_id: db_ids::UserId = user.id.into(); let project_id: db_ids::ProjectId = result.inner.id; - if !is_authorized(&result.inner, &Some(user), &pool).await? { + if !is_visible_project(&result.inner, &Some(user), &pool).await? { return Err(ApiError::NotFound); } @@ -2204,7 +2205,7 @@ pub async fn project_get_organization( ApiError::InvalidInput("The specified project does not exist!".to_string()) })?; - if is_authorized(&result.inner, &Some(user), &pool).await? { + if is_visible_project(&result.inner, &Some(user), &pool).await? { Err(ApiError::InvalidInput( "The specified project does not exist!".to_string(), )) diff --git a/src/routes/v3/teams.rs b/src/routes/v3/teams.rs index d962152e..473cd2f6 100644 --- a/src/routes/v3/teams.rs +++ b/src/routes/v3/teams.rs @@ -1,4 +1,5 @@ -use crate::auth::{get_user_from_headers, is_authorized}; +use crate::auth::checks::is_visible_project; +use crate::auth::get_user_from_headers; use crate::database::models::notification_item::NotificationBuilder; use crate::database::models::team_item::TeamAssociationId; use crate::database::models::{Organization, Team, TeamMember, User}; @@ -59,7 +60,7 @@ pub async fn team_members_get_project( .map(|x| x.1) .ok(); - if !is_authorized(&project.inner, ¤t_user, &pool).await? { + if !is_visible_project(&project.inner, ¤t_user, &pool).await? { return Err(ApiError::NotFound); } let members_data = diff --git a/src/routes/v3/version_file.rs b/src/routes/v3/version_file.rs index 3b5b15e6..2c2cbf65 100644 --- a/src/routes/v3/version_file.rs +++ b/src/routes/v3/version_file.rs @@ -1,8 +1,6 @@ use super::ApiError; -use crate::auth::{ - filter_authorized_projects, filter_authorized_versions, get_user_from_headers, - is_authorized_version, -}; +use crate::auth::checks::{filter_visible_versions, is_visible_version}; +use crate::auth::{filter_visible_projects, get_user_from_headers}; use crate::database::redis::RedisPool; use crate::models::ids::VersionId; use crate::models::pats::Scopes; @@ -67,7 +65,7 @@ pub async fn get_version_from_hash( if let Some(file) = file { let version = database::models::Version::get(file.version_id, &**pool, &redis).await?; if let Some(version) = version { - if !is_authorized_version(&version.inner, &user_option, &pool).await? { + if !is_visible_version(&version.inner, &user_option, &pool, &redis).await? { return Err(ApiError::NotFound); } @@ -179,7 +177,7 @@ pub async fn get_update_from_hash( .sorted(); if let Some(first) = versions.last() { - if !is_authorized_version(&first.inner, &user_option, &pool).await? { + if !is_visible_version(&first.inner, &user_option, &pool, &redis).await? { return Err(ApiError::NotFound); } @@ -230,11 +228,11 @@ pub async fn get_versions_from_hashes( .await?; let version_ids = files.iter().map(|x| x.version_id).collect::>(); - let versions_data = filter_authorized_versions( + let versions_data = filter_visible_versions( database::models::Version::get_many(&version_ids, &**pool, &redis).await?, &user_option, &pool, - redis, + &redis, ) .await?; @@ -283,7 +281,7 @@ pub async fn get_projects_from_hashes( let project_ids = files.iter().map(|x| x.project_id).collect::>(); - let projects_data = filter_authorized_projects( + let projects_data = filter_visible_projects( database::models::Project::get_many_ids(&project_ids, &**pool, &redis).await?, &user_option, &pool, @@ -394,7 +392,7 @@ pub async fn update_files( .last(); if let Some(version) = version { - if is_authorized_version(&version.inner, &user_option, &pool).await? { + if is_visible_version(&version.inner, &user_option, &pool, &redis).await? { if let Some(hash) = file.hashes.get(&algorithm) { response.insert( hash.clone(), @@ -516,7 +514,7 @@ pub async fn update_individual_files( .last(); if let Some(version) = version { - if is_authorized_version(&version.inner, &user_option, &pool).await? { + if is_visible_version(&version.inner, &user_option, &pool, &redis).await? { response.insert( hash.clone(), models::projects::Version::from(version.clone()), @@ -693,7 +691,7 @@ pub async fn download_version( let version = database::models::Version::get(file.version_id, &**pool, &redis).await?; if let Some(version) = version { - if !is_authorized_version(&version.inner, &user_option, &pool).await? { + if !is_visible_version(&version.inner, &user_option, &pool, &redis).await? { return Err(ApiError::NotFound); } diff --git a/src/routes/v3/versions.rs b/src/routes/v3/versions.rs index 8b65b2ef..b96ad2dd 100644 --- a/src/routes/v3/versions.rs +++ b/src/routes/v3/versions.rs @@ -1,9 +1,8 @@ use std::collections::HashMap; use super::ApiError; -use crate::auth::{ - filter_authorized_versions, get_user_from_headers, is_authorized, is_authorized_version, -}; +use crate::auth::checks::{filter_visible_versions, is_visible_project, is_visible_version}; +use crate::auth::get_user_from_headers; use crate::database; use crate::database::models::loader_fields::{ self, LoaderField, LoaderFieldEnumValue, VersionField, @@ -81,7 +80,7 @@ pub async fn version_project_get_helper( .ok(); if let Some(project) = result { - if !is_authorized(&project.inner, &user_option, &pool).await? { + if !is_visible_project(&project.inner, &user_option, &pool).await? { return Err(ApiError::NotFound); } @@ -94,7 +93,7 @@ pub async fn version_project_get_helper( .find(|x| Some(x.inner.id.0 as u64) == id_opt || x.inner.version_number == id.1); if let Some(version) = version { - if is_authorized_version(&version.inner, &user_option, &pool).await? { + if is_visible_version(&version.inner, &user_option, &pool, &redis).await? { return Ok(HttpResponse::Ok().json(models::projects::Version::from(version))); } } @@ -132,7 +131,7 @@ pub async fn versions_get( .map(|x| x.1) .ok(); - let versions = filter_authorized_versions(versions_data, &user_option, &pool, redis).await?; + let versions = filter_visible_versions(versions_data, &user_option, &pool, &redis).await?; Ok(HttpResponse::Ok().json(versions)) } @@ -169,7 +168,7 @@ pub async fn version_get_helper( .ok(); if let Some(data) = version_data { - if is_authorized_version(&data.inner, &user_option, &pool).await? { + if is_visible_version(&data.inner, &user_option, &pool, &redis).await? { return Ok(HttpResponse::Ok().json(models::projects::Version::from(data))); } } @@ -723,7 +722,7 @@ pub async fn version_list( .ok(); if let Some(project) = result { - if !is_authorized(&project.inner, &user_option, &pool).await? { + if !is_visible_project(&project.inner, &user_option, &pool).await? { return Err(ApiError::NotFound); } @@ -819,7 +818,7 @@ pub async fn version_list( response.sort(); response.dedup_by(|a, b| a.inner.id == b.inner.id); - let response = filter_authorized_versions(response, &user_option, &pool, redis).await?; + let response = filter_visible_versions(response, &user_option, &pool, &redis).await?; Ok(HttpResponse::Ok().json(response)) } else {