From 7b000039587d119d62bdf4e4d6df646e1289306f Mon Sep 17 00:00:00 2001 From: Geometrically <18202329+Geometrically@users.noreply.github.com> Date: Fri, 12 Jan 2024 14:19:39 -0500 Subject: [PATCH] Org fixes (#850) * Org fixes * payouts bug * Update dockerfile fix test * Update to bookworm * clippy --- ...5a7e4484859e1326c765e5ebd403fbc7e188b.json | 23 +++++++++ ...0a5d1741a16971b01662b9281eb5720e109b1.json | 23 +++++++++ Dockerfile | 25 +-------- docker_utils/dummy.rs | 1 - src/models/v3/teams.rs | 1 - src/queue/payouts.rs | 7 +++ src/routes/v3/organizations.rs | 2 +- src/routes/v3/project_creation.rs | 2 +- src/routes/v3/teams.rs | 20 +++++++- src/routes/v3/threads.rs | 51 ++++++++++++++++++- src/search/indexing/mod.rs | 2 +- tests/organizations.rs | 2 +- tests/teams.rs | 2 +- 13 files changed, 129 insertions(+), 32 deletions(-) create mode 100644 .sqlx/query-bad800084766fcbaf584066304a5a7e4484859e1326c765e5ebd403fbc7e188b.json create mode 100644 .sqlx/query-e4dbbb18adfd748ab7659462f940a5d1741a16971b01662b9281eb5720e109b1.json delete mode 100644 docker_utils/dummy.rs diff --git a/.sqlx/query-bad800084766fcbaf584066304a5a7e4484859e1326c765e5ebd403fbc7e188b.json b/.sqlx/query-bad800084766fcbaf584066304a5a7e4484859e1326c765e5ebd403fbc7e188b.json new file mode 100644 index 000000000..d2e35e95b --- /dev/null +++ b/.sqlx/query-bad800084766fcbaf584066304a5a7e4484859e1326c765e5ebd403fbc7e188b.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "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 tm.user_id = $2 WHERE m.id = $1)", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "exists", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [ + null + ] + }, + "hash": "bad800084766fcbaf584066304a5a7e4484859e1326c765e5ebd403fbc7e188b" +} diff --git a/.sqlx/query-e4dbbb18adfd748ab7659462f940a5d1741a16971b01662b9281eb5720e109b1.json b/.sqlx/query-e4dbbb18adfd748ab7659462f940a5d1741a16971b01662b9281eb5720e109b1.json new file mode 100644 index 000000000..516792104 --- /dev/null +++ b/.sqlx/query-e4dbbb18adfd748ab7659462f940a5d1741a16971b01662b9281eb5720e109b1.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT m.id FROM mods m\n INNER JOIN organizations o ON o.id = m.organization_id\n INNER JOIN team_members tm ON tm.team_id = o.team_id AND user_id = $2\n WHERE m.id = ANY($1)\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Int8Array", + "Int8" + ] + }, + "nullable": [ + false + ] + }, + "hash": "e4dbbb18adfd748ab7659462f940a5d1741a16971b01662b9281eb5720e109b1" +} diff --git a/Dockerfile b/Dockerfile index c6479ace7..8a2dfd5ca 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,29 +2,11 @@ FROM rust:1.75.0 as build ENV PKG_CONFIG_ALLOW_CROSS=1 WORKDIR /usr/src/labrinth -# Download and compile deps -COPY Cargo.toml . -COPY Cargo.lock . -COPY docker_utils/dummy.rs . -# Change temporarely the path of the code -RUN sed -i 's|src/main.rs|dummy.rs|' Cargo.toml -# Build only deps -RUN cargo build --release -# Now return the file back to normal -RUN sed -i 's|dummy.rs|src/main.rs|' Cargo.toml - -# Copy everything COPY . . -# Add the wait script -ADD https://github.com/ufoscout/docker-compose-wait/releases/download/2.2.1/wait /wait -RUN chmod +x /wait -# Build our code -ARG SQLX_OFFLINE=true RUN cargo build --release -# Final Stage -FROM ubuntu:latest +FROM debian:bookworm-slim RUN apt-get update \ && apt-get install -y --no-install-recommends ca-certificates \ @@ -34,9 +16,6 @@ RUN apt-get update \ RUN update-ca-certificates COPY --from=build /usr/src/labrinth/target/release/labrinth /labrinth/labrinth -COPY --from=build /usr/src/labrinth/migrations/* /labrinth/migrations/ -COPY --from=build /usr/src/labrinth/assets /labrinth/assets -COPY --from=build /wait /wait WORKDIR /labrinth -CMD /wait && /labrinth/labrinth +CMD /labrinth/labrinth \ No newline at end of file diff --git a/docker_utils/dummy.rs b/docker_utils/dummy.rs deleted file mode 100644 index e71fdf554..000000000 --- a/docker_utils/dummy.rs +++ /dev/null @@ -1 +0,0 @@ -fn main() {} \ No newline at end of file diff --git a/src/models/v3/teams.rs b/src/models/v3/teams.rs index 814705e1e..7b2716385 100644 --- a/src/models/v3/teams.rs +++ b/src/models/v3/teams.rs @@ -10,7 +10,6 @@ use serde::{Deserialize, Serialize}; #[serde(into = "Base62Id")] pub struct TeamId(pub u64); -pub const OWNER_ROLE: &str = "Owner"; pub const DEFAULT_ROLE: &str = "Member"; /// A team of users who control a project diff --git a/src/queue/payouts.rs b/src/queue/payouts.rs index 917df20b5..fe3416169 100644 --- a/src/queue/payouts.rs +++ b/src/queue/payouts.rs @@ -675,6 +675,13 @@ pub async fn process_payout( all_team_members.push((user_id, payouts_split)); } + // if all team members are set to zero, we treat as an equal revenue distribution + if all_team_members.iter().all(|x| x.1 == Decimal::ZERO) { + all_team_members + .iter_mut() + .for_each(|x| x.1 = Decimal::from(1)); + } + projects_map.insert( project_id, Project { diff --git a/src/routes/v3/organizations.rs b/src/routes/v3/organizations.rs index 7c07a6445..9d29b0a1c 100644 --- a/src/routes/v3/organizations.rs +++ b/src/routes/v3/organizations.rs @@ -145,7 +145,7 @@ pub async fn organization_create( let team = team_item::TeamBuilder { members: vec![team_item::TeamMemberBuilder { user_id: current_user.id.into(), - role: crate::models::teams::OWNER_ROLE.to_owned(), + role: crate::models::teams::DEFAULT_ROLE.to_owned(), is_owner: true, permissions: ProjectPermissions::all(), organization_permissions: Some(OrganizationPermissions::all()), diff --git a/src/routes/v3/project_creation.rs b/src/routes/v3/project_creation.rs index 96fc257f0..4e5203bff 100644 --- a/src/routes/v3/project_creation.rs +++ b/src/routes/v3/project_creation.rs @@ -617,7 +617,7 @@ async fn project_create_inner( if project_create_data.organization_id.is_none() { members.push(models::team_item::TeamMemberBuilder { user_id: current_user.id.into(), - role: crate::models::teams::OWNER_ROLE.to_owned(), + role: crate::models::teams::DEFAULT_ROLE.to_owned(), is_owner: true, permissions: ProjectPermissions::all(), organization_permissions: None, diff --git a/src/routes/v3/teams.rs b/src/routes/v3/teams.rs index ea4da134e..9191b20b2 100644 --- a/src/routes/v3/teams.rs +++ b/src/routes/v3/teams.rs @@ -504,9 +504,11 @@ pub async fn add_team_member( .as_ref() .map(|tm| tm.is_owner) .unwrap_or(false) + && new_member.permissions != ProjectPermissions::all() { return Err(ApiError::InvalidInput( - "You cannot add the owner of an organization to a project team owned by that organization".to_string(), + "You cannot override the owner of an organization's permissions in a project team" + .to_string(), )); } @@ -634,6 +636,22 @@ pub async fn edit_team_member( } else { None }; + + if organization_team_member + .as_ref() + .map(|x| x.is_owner) + .unwrap_or(false) + && edit_member + .permissions + .map(|x| x != ProjectPermissions::all()) + .unwrap_or(false) + { + return Err(ApiError::CustomAuthentication( + "You cannot override the project permissions of the organization owner!" + .to_string(), + )); + } + let permissions = ProjectPermissions::get_permissions_by_role( ¤t_user.role, &member.clone(), diff --git a/src/routes/v3/threads.rs b/src/routes/v3/threads.rs index 67c121b97..879078784 100644 --- a/src/routes/v3/threads.rs +++ b/src/routes/v3/threads.rs @@ -71,7 +71,20 @@ pub async fn is_authorized_thread( .await? .exists; - project_exists.unwrap_or(false) + if !project_exists.unwrap_or(false) { + let org_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 tm.user_id = $2 WHERE m.id = $1)", + project_id as database::models::ids::ProjectId, + user_id as database::models::ids::UserId, + ) + .fetch_one(pool) + .await? + .exists; + + org_exists.unwrap_or(false) + } else { + true + } } else { false } @@ -137,6 +150,42 @@ pub async fn filter_authorized_threads( .await?; } + let org_project_thread_ids = check_threads + .iter() + .filter(|x| x.type_ == ThreadType::Project) + .flat_map(|x| x.project_id.map(|x| x.0)) + .collect::>(); + + if !org_project_thread_ids.is_empty() { + sqlx::query!( + " + SELECT m.id FROM mods m + INNER JOIN organizations o ON o.id = m.organization_id + INNER JOIN team_members tm ON tm.team_id = o.team_id AND user_id = $2 + WHERE m.id = ANY($1) + ", + &*project_thread_ids, + user_id as database::models::ids::UserId, + ) + .fetch_many(&***pool) + .try_for_each(|e| { + if let Some(row) = e.right() { + check_threads.retain(|x| { + let bool = x.project_id.map(|x| x.0) == Some(row.id); + + if bool { + return_threads.push(x.clone()); + } + + !bool + }); + } + + futures::future::ready(Ok(())) + }) + .await?; + } + let report_thread_ids = check_threads .iter() .filter(|x| x.type_ == ThreadType::Report) diff --git a/src/search/indexing/mod.rs b/src/search/indexing/mod.rs index 5acae53fe..dd542930a 100644 --- a/src/search/indexing/mod.rs +++ b/src/search/indexing/mod.rs @@ -302,7 +302,7 @@ async fn update_and_add_to_index( // Check if any 'additional_fields' are not already in the index // Only add if they are not already in the index let new_fields = additional_fields - .into_iter() + .iter() .filter(|x| !new_filterable_attributes.contains(x)) .collect::>(); if !new_fields.is_empty() { diff --git a/tests/organizations.rs b/tests/organizations.rs index 360acfc07..beb68b43a 100644 --- a/tests/organizations.rs +++ b/tests/organizations.rs @@ -95,7 +95,7 @@ async fn create_organization() { members[0].organization_permissions, Some(OrganizationPermissions::all()) ); - assert_eq!(members[0].role, "Owner"); + assert_eq!(members[0].role, "Member"); assert!(members[0].is_owner); }) .await; diff --git a/tests/teams.rs b/tests/teams.rs index a1f2fe57e..b1ff73fdc 100644 --- a/tests/teams.rs +++ b/tests/teams.rs @@ -535,7 +535,7 @@ async fn transfer_ownership_v3() { .iter() .find(|x| x.user.id.0 == USER_USER_ID_PARSED as u64) .unwrap(); - assert_eq!(user_member.role, "Owner"); // We are the 'owner', but we are not actually the owner! + assert_eq!(user_member.role, "Member"); // We are the 'owner', but we are not actually the owner! assert!(!user_member.is_owner); assert_eq!(user_member.permissions.unwrap(), ProjectPermissions::all());