Organizations (#712)

* untested, unformatted, un-refactored

* minor simplification

* simplification fix

* refactoring, changes

* some fixes

* fixes, refactoring

* missed cache

* revs

* revs - more!

* removed donation links; added all org members to route

* renamed slug to title

---------

Co-authored-by: Geometrically <18202329+Geometrically@users.noreply.github.com>
This commit is contained in:
Wyatt Verchere
2023-10-02 10:56:57 -07:00
committed by GitHub
parent 58a61051b9
commit a1b59d4545
24 changed files with 3658 additions and 979 deletions

View File

@@ -24,7 +24,7 @@ pub struct Team {
bitflags::bitflags! {
#[derive(Serialize, Deserialize)]
#[serde(transparent)]
pub struct Permissions: u64 {
pub struct ProjectPermissions: u64 {
const UPLOAD_VERSION = 1 << 0;
const DELETE_VERSION = 1 << 1;
const EDIT_DETAILS = 1 << 2;
@@ -40,26 +40,86 @@ bitflags::bitflags! {
}
}
impl Default for Permissions {
fn default() -> Permissions {
Permissions::UPLOAD_VERSION | Permissions::DELETE_VERSION
impl Default for ProjectPermissions {
fn default() -> ProjectPermissions {
ProjectPermissions::UPLOAD_VERSION | ProjectPermissions::DELETE_VERSION
}
}
impl Permissions {
impl ProjectPermissions {
pub fn get_permissions_by_role(
role: &crate::models::users::Role,
project_team_member: &Option<crate::database::models::TeamMember>, // team member of the user in the project
organization_team_member: &Option<crate::database::models::TeamMember>, // 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 {
return Some(member.permissions);
}
if let Some(member) = organization_team_member {
return Some(member.permissions); // Use default project permissions for the organization team member
}
if role.is_mod() {
Some(
ProjectPermissions::EDIT_DETAILS
| ProjectPermissions::EDIT_BODY
| ProjectPermissions::UPLOAD_VERSION,
)
} else {
None
}
}
}
bitflags::bitflags! {
#[derive(Serialize, Deserialize)]
#[serde(transparent)]
pub struct OrganizationPermissions: u64 {
const EDIT_DETAILS = 1 << 0;
const EDIT_BODY = 1 << 1;
const MANAGE_INVITES = 1 << 2;
const REMOVE_MEMBER = 1 << 3;
const EDIT_MEMBER = 1 << 4;
const ADD_PROJECT = 1 << 5;
const REMOVE_PROJECT = 1 << 6;
const DELETE_ORGANIZATION = 1 << 8;
const EDIT_MEMBER_DEFAULT_PERMISSIONS = 1 << 9; // Separate from EDIT_MEMBER
const ALL = 0b1111111111;
const NONE = 0b0;
}
}
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::TeamMember>,
) -> Option<Self> {
if role.is_admin() {
Some(Permissions::ALL)
} else if let Some(member) = team_member {
Some(member.permissions)
} else if role.is_mod() {
Some(Permissions::EDIT_DETAILS | Permissions::EDIT_BODY | Permissions::UPLOAD_VERSION)
} else {
None
return Some(OrganizationPermissions::ALL);
}
if let Some(member) = team_member {
return member.organization_permissions;
}
if role.is_mod() {
return Some(
OrganizationPermissions::EDIT_DETAILS
| OrganizationPermissions::EDIT_BODY
| OrganizationPermissions::ADD_PROJECT,
);
}
None
}
}
@@ -72,8 +132,16 @@ pub struct TeamMember {
pub user: User,
/// The role of the user in the team
pub role: String,
/// A bitset containing the user's permissions in this team
pub permissions: Option<Permissions>,
/// 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,
@@ -92,7 +160,17 @@ impl TeamMember {
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::TeamMember,
user: crate::models::users::User,
override_permissions: bool,
) -> Self {
Self {
team_id: data.team_id.into(),
user,
@@ -102,6 +180,11 @@ impl TeamMember {
} else {
Some(data.permissions)
},
organization_permissions: if override_permissions {
None
} else {
data.organization_permissions
},
accepted: data.accepted,
payouts_split: if override_permissions {
None