Files
AstralRinth/apps/labrinth/src/models/v3/teams.rs
Josiah Glosson cf190d86d5 Update Rust dependencies (#4139)
* Update Rust version

* Update async-compression 0.4.25 -> 0.4.27

* Update async-tungstenite 0.29.1 -> 0.30.0

* Update bytemuck 1.23.0 -> 1.23.1

* Update clap 4.5.40 -> 4.5.43

* Update deadpool-redis 0.21.1 -> 0.22.0 and redis 0.31.0 -> 0.32.4

* Update enumset 1.1.6 -> 1.1.7

* Update hyper-util 0.1.14 -> 0.1.16

* Update indexmap 2.9.0 -> 2.10.0

* Update indicatif 0.17.11 -> 0.18.0

* Update jemalloc_pprof 0.7.0 -> 0.8.1

* Update lettre 0.11.17 -> 0.11.18

* Update meilisearch-sdk 0.28.0 -> 0.29.1

* Update notify 8.0.0 -> 8.2.0 and notify-debouncer-mini 0.6.0 -> 0.7.0

* Update quick-xml 0.37.5 -> 0.38.1

* Fix theseus lint

* Update reqwest 0.12.20 -> 0.12.22

* Cargo fmt in theseus

* Update rgb 0.8.50 -> 0.8.52

* Update sentry 0.41.0 -> 0.42.0 and sentry-actix 0.41.0 -> 0.42.0

* Update serde_json 1.0.140 -> 1.0.142

* Update serde_with 3.13.0 -> 3.14.0

* Update spdx 0.10.8 -> 0.10.9

* Update sysinfo 0.35.2 -> 0.36.1

* Update tauri suite

* Fix build by updating mappings

* Update tokio 1.45.1 -> 1.47.1 and tokio-util 0.7.15 -> 0.7.16

* Update tracing-actix-web 0.7.18 -> 0.7.19

* Update zip 4.2.0 -> 4.3.0

* Misc Cargo.lock updates

* Update Dockerfiles
2025-08-08 22:50:44 +00:00

200 lines
6.1 KiB
Rust

use crate::bitflags_serde_impl;
use crate::models::ids::TeamId;
use crate::models::users::User;
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
pub const DEFAULT_ROLE: &str = "Member";
/// A team of users who control a project
#[derive(Serialize, Deserialize)]
pub struct Team {
/// The id of the team
pub id: TeamId,
/// A list of the members of the team
pub members: Vec<TeamMember>,
}
bitflags::bitflags! {
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct ProjectPermissions: u64 {
const UPLOAD_VERSION = 1 << 0;
const DELETE_VERSION = 1 << 1;
const EDIT_DETAILS = 1 << 2;
const EDIT_BODY = 1 << 3;
const MANAGE_INVITES = 1 << 4;
const REMOVE_MEMBER = 1 << 5;
const EDIT_MEMBER = 1 << 6;
const DELETE_PROJECT = 1 << 7;
const VIEW_ANALYTICS = 1 << 8;
const VIEW_PAYOUTS = 1 << 9;
}
}
bitflags_serde_impl!(ProjectPermissions, u64);
impl Default for ProjectPermissions {
fn default() -> ProjectPermissions {
ProjectPermissions::empty()
}
}
impl ProjectPermissions {
pub fn get_permissions_by_role(
role: &crate::models::users::Role,
project_team_member: &Option<crate::database::models::DBTeamMember>, // team member of the user in the project
organization_team_member: &Option<
crate::database::models::DBTeamMember,
>, // team member of the user in the organization
) -> Option<Self> {
if role.is_admin() {
return Some(ProjectPermissions::all());
}
if let Some(member) = project_team_member
&& member.accepted
{
return Some(member.permissions);
}
if let Some(member) = organization_team_member
&& member.accepted
{
return Some(member.permissions);
}
if role.is_mod() {
Some(
ProjectPermissions::EDIT_DETAILS
| ProjectPermissions::EDIT_BODY
| ProjectPermissions::UPLOAD_VERSION,
)
} else {
None
}
}
}
bitflags::bitflags! {
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct OrganizationPermissions: u64 {
const EDIT_DETAILS = 1 << 0;
const MANAGE_INVITES = 1 << 1;
const REMOVE_MEMBER = 1 << 2;
const EDIT_MEMBER = 1 << 3;
const ADD_PROJECT = 1 << 4;
const REMOVE_PROJECT = 1 << 5;
const DELETE_ORGANIZATION = 1 << 6;
const EDIT_MEMBER_DEFAULT_PERMISSIONS = 1 << 7; // Separate from EDIT_MEMBER
const NONE = 0b0;
}
}
bitflags_serde_impl!(OrganizationPermissions, u64);
impl Default for OrganizationPermissions {
fn default() -> OrganizationPermissions {
OrganizationPermissions::NONE
}
}
impl OrganizationPermissions {
pub fn get_permissions_by_role(
role: &crate::models::users::Role,
team_member: &Option<crate::database::models::DBTeamMember>,
) -> Option<Self> {
if role.is_admin() {
return Some(OrganizationPermissions::all());
}
if let Some(member) = team_member
&& member.accepted
{
return member.organization_permissions;
}
if role.is_mod() {
return Some(
OrganizationPermissions::EDIT_DETAILS
| OrganizationPermissions::ADD_PROJECT,
);
}
None
}
}
/// A member of a team
#[derive(Serialize, Deserialize, Clone)]
pub struct TeamMember {
/// The ID of the team this team member is a member of
pub team_id: TeamId,
/// The user associated with the member
pub user: User,
/// The role of the user in the team
pub role: String,
/// Is the user the owner of the team?
pub is_owner: bool,
/// A bitset containing the user's permissions in this team.
/// In an organization-controlled project, these are the unique overriding permissions for the user's role for any project in the organization, if they exist.
/// In an organization, these are the default project permissions for any project in the organization.
/// Not optional- only None if they are being hidden from the user.
pub permissions: Option<ProjectPermissions>,
/// A bitset containing the user's permissions in this organization.
/// In a project team, this is None.
pub organization_permissions: Option<OrganizationPermissions>,
/// Whether the user has joined the team or is just invited to it
pub accepted: bool,
#[serde(with = "rust_decimal::serde::float_option")]
/// Payouts split. This is a weighted average. For example. if a team has two members with this
/// value set to 25.0 for both members, they split revenue 50/50
pub payouts_split: Option<Decimal>,
/// Ordering of the member in the list
pub ordering: i64,
}
impl TeamMember {
pub fn from(
data: crate::database::models::team_item::DBTeamMember,
user: crate::database::models::DBUser,
override_permissions: bool,
) -> Self {
let user: User = user.into();
Self::from_model(data, user, override_permissions)
}
// Use the User model directly instead of the database model,
// if already available.
// (Avoids a db query in some cases)
pub fn from_model(
data: crate::database::models::team_item::DBTeamMember,
user: crate::models::users::User,
override_permissions: bool,
) -> Self {
Self {
team_id: data.team_id.into(),
user,
role: data.role,
is_owner: data.is_owner,
permissions: if override_permissions {
None
} else {
Some(data.permissions)
},
organization_permissions: if override_permissions {
None
} else {
data.organization_permissions
},
accepted: data.accepted,
payouts_split: if override_permissions {
None
} else {
Some(data.payouts_split)
},
ordering: data.ordering,
}
}
}