Filtering refactoring (#806)

* switching computers

* fmt clippy sqlx prepare

* merge fixes
This commit is contained in:
Wyatt Verchere
2023-12-21 16:36:30 -08:00
committed by GitHub
parent b46f3bf2c4
commit 76e00c2432
18 changed files with 293 additions and 392 deletions

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -7,6 +7,7 @@ use crate::database::{models, Project, Version};
use crate::models::users::User; use crate::models::users::User;
use crate::routes::ApiError; use crate::routes::ApiError;
use actix_web::web; use actix_web::web;
use itertools::Itertools;
use sqlx::PgPool; use sqlx::PgPool;
pub trait ValidateAuthorized { pub trait ValidateAuthorized {
@@ -28,179 +29,168 @@ where
} }
} }
pub async fn is_authorized( pub async fn is_visible_project(
project_data: &Project, project_data: &Project,
user_option: &Option<User>, user_option: &Option<User>,
pool: &web::Data<PgPool>, pool: &web::Data<PgPool>,
) -> Result<bool, ApiError> { ) -> Result<bool, ApiError> {
let mut authorized = !project_data.status.is_hidden(); filter_visible_project_ids(vec![project_data], user_option, pool)
.await
if let Some(user) = &user_option { .map(|x| !x.is_empty())
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);
}
}
}
Ok(authorized)
} }
pub async fn filter_authorized_projects( pub async fn is_team_member_project(
projects: Vec<QueryProject>, project_data: &Project,
user_option: &Option<User>,
pool: &web::Data<PgPool>,
) -> Result<bool, ApiError> {
filter_enlisted_projects_ids(vec![project_data], user_option, pool)
.await
.map(|x| !x.is_empty())
}
pub async fn filter_visible_projects(
mut projects: Vec<QueryProject>,
user_option: &Option<User>, user_option: &Option<User>,
pool: &web::Data<PgPool>, pool: &web::Data<PgPool>,
) -> Result<Vec<crate::models::projects::Project>, ApiError> { ) -> Result<Vec<crate::models::projects::Project>, 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<User>,
pool: &web::Data<PgPool>,
) -> Result<Vec<crate::database::models::ProjectId>, ApiError> {
let mut return_projects = Vec::new(); let mut return_projects = Vec::new();
let mut check_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 { for project in projects {
if !project.inner.status.is_hidden() if !project.status.is_hidden()
|| user_option || user_option
.as_ref() .as_ref()
.map(|x| x.role.is_mod()) .map(|x| x.role.is_mod())
.unwrap_or(false) .unwrap_or(false)
{ {
return_projects.push(project.into()); return_projects.push(project.id);
} else if user_option.is_some() { } else if user_option.is_some() {
check_projects.push(project); 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 !check_projects.is_empty() {
if let Some(user) = user_option { return_projects
let user_id: models::ids::UserId = user.id.into(); .extend(filter_enlisted_projects_ids(check_projects, user_option, pool).await?);
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::<Vec<_>>(),
&check_projects
.iter()
.filter_map(|x| x.inner.organization_id.map(|x| x.0))
.collect::<Vec<_>>(),
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
});
}
futures::future::ready(Ok(()))
})
.await?;
}
} }
Ok(return_projects) Ok(return_projects)
} }
pub async fn is_authorized_version( // 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<User>,
pool: &web::Data<PgPool>,
) -> Result<Vec<crate::database::models::ProjectId>, 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::<Vec<_>>(),
&projects
.iter()
.filter_map(|x| x.organization_id.map(|x| x.0))
.collect::<Vec<_>>(),
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?;
}
Ok(return_projects)
}
pub async fn is_visible_version(
version_data: &Version, version_data: &Version,
user_option: &Option<User>, user_option: &Option<User>,
pool: &web::Data<PgPool>, pool: &web::Data<PgPool>,
redis: &RedisPool,
) -> Result<bool, ApiError> { ) -> Result<bool, ApiError> {
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 { pub async fn is_team_member_version(
if !authorized { version_data: &Version,
if user.role.is_mod() { user_option: &Option<User>,
authorized = true; pool: &web::Data<PgPool>,
} else { redis: &RedisPool,
let user_id: models::ids::UserId = user.id.into(); ) -> Result<bool, ApiError> {
filter_enlisted_version_ids(vec![version_data], user_option, pool, redis)
.await
.map(|x| !x.is_empty())
}
let version_exists = sqlx::query!( pub async fn filter_visible_versions(
"SELECT EXISTS( mut versions: Vec<QueryVersion>,
SELECT 1 FROM mods m user_option: &Option<User>,
INNER JOIN team_members tm ON tm.team_id = m.team_id AND user_id = $2 pool: &web::Data<PgPool>,
WHERE m.id = $1 redis: &RedisPool,
)", ) -> Result<Vec<crate::models::projects::Version>, ApiError> {
version_data.project_id as database::models::ids::ProjectId, let filtered_version_ids = filter_visible_version_ids(
user_id as database::models::ids::UserId, versions.iter().map(|x| &x.inner).collect_vec(),
) user_option,
.fetch_one(&***pool) pool,
.await? redis,
.exists; )
.await
let version_organization_exists = sqlx::query!( .unwrap();
"SELECT EXISTS( versions.retain(|x| filtered_version_ids.contains(&x.inner.id));
SELECT 1 FROM mods m Ok(versions.into_iter().map(|x| x.into()).collect())
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);
}
}
}
Ok(authorized)
} }
impl ValidateAuthorized for models::OAuthClient { impl ValidateAuthorized for models::OAuthClient {
@@ -220,62 +210,111 @@ impl ValidateAuthorized for models::OAuthClient {
} }
} }
pub async fn filter_authorized_versions( pub async fn filter_visible_version_ids(
versions: Vec<QueryVersion>, versions: Vec<&Version>,
user_option: &Option<User>, user_option: &Option<User>,
pool: &web::Data<PgPool>, pool: &web::Data<PgPool>,
redis: web::Data<RedisPool>, redis: &RedisPool,
) -> Result<Vec<crate::models::projects::Version>, ApiError> { ) -> Result<Vec<crate::database::models::VersionId>, ApiError> {
let mut return_versions = Vec::new(); let mut return_versions = Vec::new();
let mut check_versions = Vec::new();
let project_ids = versions // First, filter out versions belonging to projects we can't see
.iter() // (ie: a hidden project, but public version, should still be hidden)
.map(|x| x.inner.project_id) // Gets project ids of versions
.collect::<Vec<_>>(); let project_ids = versions.iter().map(|x| x.project_id).collect::<Vec<_>>();
let authorized_projects = filter_authorized_projects( // Get visible projects- ones we are allowed to see public versions for.
Project::get_many_ids(&project_ids, &***pool, &redis).await?, 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, user_option,
pool, pool,
) )
.await?; .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 { 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 || user_option
.as_ref() .as_ref()
.map(|x| x.role.is_mod()) .map(|x| x.role.is_mod())
.unwrap_or(false) .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.into()); return_versions.push(version.id);
} else if user_option.is_some() {
check_versions.push(version);
} }
} }
Ok(return_versions) Ok(return_versions)
} }
pub async fn is_authorized_collection( pub async fn filter_enlisted_version_ids(
versions: Vec<&Version>,
user_option: &Option<User>,
pool: &web::Data<PgPool>,
redis: &RedisPool,
) -> Result<Vec<crate::database::models::VersionId>, ApiError> {
let mut return_versions = Vec::new();
// Get project ids of versions
let project_ids = versions.iter().map(|x| x.project_id).collect::<Vec<_>>();
// 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.id);
}
}
Ok(return_versions)
}
pub async fn is_visible_collection(
collection_data: &Collection, collection_data: &Collection,
user_option: &Option<User>, user_option: &Option<User>,
) -> Result<bool, ApiError> { ) -> Result<bool, ApiError> {
let mut authorized = !collection_data.status.is_hidden(); let mut authorized = !collection_data.status.is_hidden();
if let Some(user) = &user_option { if let Some(user) = &user_option {
if !authorized && (user.role.is_mod() || user.id == collection_data.user_id.into()) { if !authorized && (user.role.is_mod() || user.id == collection_data.user_id.into()) {
authorized = true; authorized = true;
} }
} }
Ok(authorized) Ok(authorized)
} }
pub async fn filter_authorized_collections( pub async fn filter_visible_collections(
collections: Vec<Collection>, collections: Vec<Collection>,
user_option: &Option<User>, user_option: &Option<User>,
pool: &web::Data<PgPool>,
) -> Result<Vec<crate::models::collections::Collection>, ApiError> { ) -> Result<Vec<crate::models::collections::Collection>, ApiError> {
let mut return_collections = Vec::new(); let mut return_collections = Vec::new();
let mut check_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 { if let Some(user) = user_option {
let user_id: models::ids::UserId = user.id.into(); if user.role.is_mod() || user.id == collection.user_id.into() {
return_collections.push(collection.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::<Vec<_>>(),
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?;
} }
} }

View File

@@ -4,7 +4,8 @@ pub mod oauth;
pub mod templates; pub mod templates;
pub mod validate; pub mod validate;
pub use checks::{ 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}; use serde::{Deserialize, Serialize};
// pub use pat::{generate_pat, PersonalAccessToken}; // pub use pat::{generate_pat, PersonalAccessToken};

View File

@@ -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::legacy_loader_fields::MinecraftGameVersion;
use crate::database::models::loader_fields::Loader; use crate::database::models::loader_fields::Loader;
use crate::database::models::project_item::QueryProject; use crate::database::models::project_item::QueryProject;
@@ -7,10 +8,7 @@ use crate::models::pats::Scopes;
use crate::models::projects::{ProjectId, VersionId}; use crate::models::projects::{ProjectId, VersionId};
use crate::queue::session::AuthQueue; use crate::queue::session::AuthQueue;
use crate::routes::ApiError; use crate::routes::ApiError;
use crate::{ use crate::{auth::get_user_from_headers, database};
auth::{get_user_from_headers, is_authorized, is_authorized_version},
database,
};
use actix_web::{get, route, web, HttpRequest, HttpResponse}; use actix_web::{get, route, web, HttpRequest, HttpResponse};
use sqlx::PgPool; use sqlx::PgPool;
use std::collections::HashSet; use std::collections::HashSet;
@@ -94,7 +92,7 @@ pub async fn maven_metadata(
.map(|x| x.1) .map(|x| x.1)
.ok(); .ok();
if !is_authorized(&project.inner, &user_option, &pool).await? { if !is_visible_project(&project.inner, &user_option, &pool).await? {
return Err(ApiError::NotFound); return Err(ApiError::NotFound);
} }
@@ -288,7 +286,7 @@ pub async fn version_file(
.map(|x| x.1) .map(|x| x.1)
.ok(); .ok();
if !is_authorized(&project.inner, &user_option, &pool).await? { if !is_visible_project(&project.inner, &user_option, &pool).await? {
return Err(ApiError::NotFound); return Err(ApiError::NotFound);
} }
@@ -296,7 +294,7 @@ pub async fn version_file(
return Err(ApiError::NotFound); 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); return Err(ApiError::NotFound);
} }
@@ -349,7 +347,7 @@ pub async fn version_file_sha1(
.map(|x| x.1) .map(|x| x.1)
.ok(); .ok();
if !is_authorized(&project.inner, &user_option, &pool).await? { if !is_visible_project(&project.inner, &user_option, &pool).await? {
return Err(ApiError::NotFound); return Err(ApiError::NotFound);
} }
@@ -357,7 +355,7 @@ pub async fn version_file_sha1(
return Err(ApiError::NotFound); 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); return Err(ApiError::NotFound);
} }
@@ -391,7 +389,7 @@ pub async fn version_file_sha512(
.map(|x| x.1) .map(|x| x.1)
.ok(); .ok();
if !is_authorized(&project.inner, &user_option, &pool).await? { if !is_visible_project(&project.inner, &user_option, &pool).await? {
return Err(ApiError::NotFound); return Err(ApiError::NotFound);
} }
@@ -399,7 +397,7 @@ pub async fn version_file_sha512(
return Err(ApiError::NotFound); 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); return Err(ApiError::NotFound);
} }

View File

@@ -4,7 +4,8 @@ use actix_web::{get, web, HttpRequest, HttpResponse};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sqlx::PgPool; 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;
use crate::database::models::legacy_loader_fields::MinecraftGameVersion; use crate::database::models::legacy_loader_fields::MinecraftGameVersion;
use crate::database::redis::RedisPool; use crate::database::redis::RedisPool;
@@ -56,7 +57,7 @@ pub async fn forge_updates(
.map(|x| x.1) .map(|x| x.1)
.ok(); .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())); return Err(ApiError::InvalidInput(ERROR.to_string()));
} }
@@ -68,14 +69,14 @@ pub async fn forge_updates(
_ => |x: &String| *x == "forge", _ => |x: &String| *x == "forge",
}; };
let mut versions = filter_authorized_versions( let mut versions = filter_visible_versions(
versions versions
.into_iter() .into_iter()
.filter(|x| x.loaders.iter().any(loaders)) .filter(|x| x.loaders.iter().any(loaders))
.collect(), .collect(),
&user_option, &user_option,
&pool, &pool,
redis, &redis,
) )
.await?; .await?;

View File

@@ -1,5 +1,5 @@
use crate::auth::checks::{filter_authorized_collections, is_authorized_collection}; use crate::auth::checks::is_visible_collection;
use crate::auth::get_user_from_headers; use crate::auth::{filter_visible_collections, get_user_from_headers};
use crate::database::models::{collection_item, generate_collection_id, project_item}; use crate::database::models::{collection_item, generate_collection_id, project_item};
use crate::database::redis::RedisPool; use crate::database::redis::RedisPool;
use crate::file_hosting::FileHost; use crate::file_hosting::FileHost;
@@ -155,7 +155,7 @@ pub async fn collections_get(
.map(|x| x.1) .map(|x| x.1)
.ok(); .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)) Ok(HttpResponse::Ok().json(collections))
} }
@@ -183,7 +183,7 @@ pub async fn collection_get(
.ok(); .ok();
if let Some(data) = collection_data { 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))); return Ok(HttpResponse::Ok().json(Collection::from(data)));
} }
} }

View File

@@ -1,6 +1,7 @@
use std::sync::Arc; 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;
use crate::database::models::{project_item, report_item, thread_item, version_item}; use crate::database::models::{project_item, report_item, thread_item, version_item};
use crate::database::redis::RedisPool; use crate::database::redis::RedisPool;
@@ -62,7 +63,9 @@ pub async fn images_add(
if let Some(id) = data.project_id { if let Some(id) = data.project_id {
let project = project_item::Project::get(&id, &**pool, &redis).await?; let project = project_item::Project::get(&id, &**pool, &redis).await?;
if let Some(project) = project { 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()); *project_id = Some(project.inner.id.into());
} else { } else {
return Err(ApiError::CustomAuthentication( return Err(ApiError::CustomAuthentication(
@@ -81,7 +84,13 @@ pub async fn images_add(
if let Some(id) = data.version_id { if let Some(id) = data.version_id {
let version = version_item::Version::get(id.into(), &**pool, &redis).await?; let version = version_item::Version::get(id.into(), &**pool, &redis).await?;
if let Some(version) = version { 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()); *version_id = Some(version.inner.id.into());
} else { } else {

View File

@@ -2,7 +2,7 @@ use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use super::ApiError; 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::team_item::TeamMember;
use crate::database::models::{generate_organization_id, team_item, Organization}; use crate::database::models::{generate_organization_id, team_item, Organization};
use crate::database::redis::RedisPool; use crate::database::redis::RedisPool;
@@ -85,7 +85,7 @@ pub async fn organization_projects_get(
let projects_data = let projects_data =
crate::database::models::Project::get_many_ids(&project_ids, &**pool, &redis).await?; crate::database::models::Project::get_many_ids(&project_ids, &**pool, &redis).await?;
let projects = filter_authorized_projects(projects_data, &current_user, &pool).await?; let projects = filter_visible_projects(projects_data, &current_user, &pool).await?;
Ok(HttpResponse::Ok().json(projects)) Ok(HttpResponse::Ok().json(projects))
} }

View File

@@ -1,7 +1,8 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; 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::notification_item::NotificationBuilder;
use crate::database::models::project_item::{GalleryItem, ModCategory}; use crate::database::models::project_item::{GalleryItem, ModCategory};
use crate::database::models::thread_item::ThreadMessageBuilder; use crate::database::models::thread_item::ThreadMessageBuilder;
@@ -136,7 +137,7 @@ pub async fn projects_get(
.map(|x| x.1) .map(|x| x.1)
.ok(); .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)) Ok(HttpResponse::Ok().json(projects))
} }
@@ -163,7 +164,7 @@ pub async fn project_get(
.ok(); .ok();
if let Some(data) = project_data { 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))); return Ok(HttpResponse::Ok().json(Project::from(data)));
} }
} }
@@ -968,7 +969,7 @@ pub async fn dependency_list(
.ok(); .ok();
if let Some(project) = result { 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); return Err(ApiError::NotFound);
} }
@@ -2061,7 +2062,7 @@ pub async fn project_follow(
let user_id: db_ids::UserId = user.id.into(); let user_id: db_ids::UserId = user.id.into();
let project_id: db_ids::ProjectId = result.inner.id; 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); return Err(ApiError::NotFound);
} }
@@ -2204,7 +2205,7 @@ pub async fn project_get_organization(
ApiError::InvalidInput("The specified project does not exist!".to_string()) 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( Err(ApiError::InvalidInput(
"The specified project does not exist!".to_string(), "The specified project does not exist!".to_string(),
)) ))

View File

@@ -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::notification_item::NotificationBuilder;
use crate::database::models::team_item::TeamAssociationId; use crate::database::models::team_item::TeamAssociationId;
use crate::database::models::{Organization, Team, TeamMember, User}; use crate::database::models::{Organization, Team, TeamMember, User};
@@ -59,7 +60,7 @@ pub async fn team_members_get_project(
.map(|x| x.1) .map(|x| x.1)
.ok(); .ok();
if !is_authorized(&project.inner, &current_user, &pool).await? { if !is_visible_project(&project.inner, &current_user, &pool).await? {
return Err(ApiError::NotFound); return Err(ApiError::NotFound);
} }
let members_data = let members_data =

View File

@@ -1,8 +1,6 @@
use super::ApiError; use super::ApiError;
use crate::auth::{ use crate::auth::checks::{filter_visible_versions, is_visible_version};
filter_authorized_projects, filter_authorized_versions, get_user_from_headers, use crate::auth::{filter_visible_projects, get_user_from_headers};
is_authorized_version,
};
use crate::database::redis::RedisPool; use crate::database::redis::RedisPool;
use crate::models::ids::VersionId; use crate::models::ids::VersionId;
use crate::models::pats::Scopes; use crate::models::pats::Scopes;
@@ -67,7 +65,7 @@ pub async fn get_version_from_hash(
if let Some(file) = file { if let Some(file) = file {
let version = database::models::Version::get(file.version_id, &**pool, &redis).await?; let version = database::models::Version::get(file.version_id, &**pool, &redis).await?;
if let Some(version) = version { 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); return Err(ApiError::NotFound);
} }
@@ -179,7 +177,7 @@ pub async fn get_update_from_hash(
.sorted(); .sorted();
if let Some(first) = versions.last() { 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); return Err(ApiError::NotFound);
} }
@@ -230,11 +228,11 @@ pub async fn get_versions_from_hashes(
.await?; .await?;
let version_ids = files.iter().map(|x| x.version_id).collect::<Vec<_>>(); let version_ids = files.iter().map(|x| x.version_id).collect::<Vec<_>>();
let versions_data = filter_authorized_versions( let versions_data = filter_visible_versions(
database::models::Version::get_many(&version_ids, &**pool, &redis).await?, database::models::Version::get_many(&version_ids, &**pool, &redis).await?,
&user_option, &user_option,
&pool, &pool,
redis, &redis,
) )
.await?; .await?;
@@ -283,7 +281,7 @@ pub async fn get_projects_from_hashes(
let project_ids = files.iter().map(|x| x.project_id).collect::<Vec<_>>(); let project_ids = files.iter().map(|x| x.project_id).collect::<Vec<_>>();
let projects_data = filter_authorized_projects( let projects_data = filter_visible_projects(
database::models::Project::get_many_ids(&project_ids, &**pool, &redis).await?, database::models::Project::get_many_ids(&project_ids, &**pool, &redis).await?,
&user_option, &user_option,
&pool, &pool,
@@ -394,7 +392,7 @@ pub async fn update_files(
.last(); .last();
if let Some(version) = version { 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) { if let Some(hash) = file.hashes.get(&algorithm) {
response.insert( response.insert(
hash.clone(), hash.clone(),
@@ -516,7 +514,7 @@ pub async fn update_individual_files(
.last(); .last();
if let Some(version) = version { 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( response.insert(
hash.clone(), hash.clone(),
models::projects::Version::from(version.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?; let version = database::models::Version::get(file.version_id, &**pool, &redis).await?;
if let Some(version) = version { 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); return Err(ApiError::NotFound);
} }

View File

@@ -1,9 +1,8 @@
use std::collections::HashMap; use std::collections::HashMap;
use super::ApiError; use super::ApiError;
use crate::auth::{ use crate::auth::checks::{filter_visible_versions, is_visible_project, is_visible_version};
filter_authorized_versions, get_user_from_headers, is_authorized, is_authorized_version, use crate::auth::get_user_from_headers;
};
use crate::database; use crate::database;
use crate::database::models::loader_fields::{ use crate::database::models::loader_fields::{
self, LoaderField, LoaderFieldEnumValue, VersionField, self, LoaderField, LoaderFieldEnumValue, VersionField,
@@ -81,7 +80,7 @@ pub async fn version_project_get_helper(
.ok(); .ok();
if let Some(project) = result { 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); 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); .find(|x| Some(x.inner.id.0 as u64) == id_opt || x.inner.version_number == id.1);
if let Some(version) = version { 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))); return Ok(HttpResponse::Ok().json(models::projects::Version::from(version)));
} }
} }
@@ -132,7 +131,7 @@ pub async fn versions_get(
.map(|x| x.1) .map(|x| x.1)
.ok(); .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)) Ok(HttpResponse::Ok().json(versions))
} }
@@ -169,7 +168,7 @@ pub async fn version_get_helper(
.ok(); .ok();
if let Some(data) = version_data { 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))); return Ok(HttpResponse::Ok().json(models::projects::Version::from(data)));
} }
} }
@@ -723,7 +722,7 @@ pub async fn version_list(
.ok(); .ok();
if let Some(project) = result { 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); return Err(ApiError::NotFound);
} }
@@ -819,7 +818,7 @@ pub async fn version_list(
response.sort(); response.sort();
response.dedup_by(|a, b| a.inner.id == b.inner.id); 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)) Ok(HttpResponse::Ok().json(response))
} else { } else {