From 9de963fb56c4b36857f5e4505bd689f98c216f37 Mon Sep 17 00:00:00 2001 From: Paul Makles Date: Sun, 7 Apr 2024 21:35:45 +0100 Subject: [PATCH] refactor(quark): port member_edit, member_experimental_query, member_fetch_all, member_remove, permissions_set_default, permissions_set #283 --- .../core/database/src/models/users/model.rs | 24 +++- crates/core/database/src/util/bridge/v0.rs | 21 +++- crates/core/models/src/v0/server_members.rs | 52 ++++++++- crates/core/models/src/v0/servers.rs | 8 +- .../src/routes/channels/members_fetch.rs | 8 +- .../delta/src/routes/invites/invite_join.rs | 9 +- .../delta/src/routes/servers/member_edit.rs | 106 ++++++++---------- .../servers/member_experimental_query.rs | 44 +++++--- .../delta/src/routes/servers/member_fetch.rs | 47 +++++--- .../src/routes/servers/member_fetch_all.rs | 65 +++++------ .../delta/src/routes/servers/member_remove.rs | 41 ++++--- crates/delta/src/routes/servers/mod.rs | 12 +- .../src/routes/servers/permissions_set.rs | 47 ++++---- .../routes/servers/permissions_set_default.rs | 40 ++++--- 14 files changed, 308 insertions(+), 216 deletions(-) diff --git a/crates/core/database/src/models/users/model.rs b/crates/core/database/src/models/users/model.rs index fc7b31f6..5ae289a0 100644 --- a/crates/core/database/src/models/users/model.rs +++ b/crates/core/database/src/models/users/model.rs @@ -5,6 +5,8 @@ use crate::{events::client::EventV1, Database, File, RatelimitEvent}; use once_cell::sync::Lazy; use rand::seq::SliceRandom; use revolt_config::config; +use revolt_models::v0; +use revolt_presence::filter_online; use revolt_result::{create_error, Error, ErrorType, Result}; use ulid::Ulid; @@ -264,7 +266,27 @@ impl User { Ok(username) } - // Find a free discriminator for a given username + /// Helper function to fetch many users as a mutually connected user + /// (while optimising the online ID query) + pub async fn fetch_many_ids_as_mutuals( + db: &Database, + perspective: &User, + ids: &[String], + ) -> Result> { + let online_ids = filter_online(ids).await; + + Ok(db + .fetch_users(ids) + .await? + .into_iter() + .map(|user| { + let is_online = online_ids.contains(&user.id); + user.into_known(perspective, is_online) + }) + .collect()) + } + + /// Find a free discriminator for a given username pub async fn find_discriminator( db: &Database, username: &str, diff --git a/crates/core/database/src/util/bridge/v0.rs b/crates/core/database/src/util/bridge/v0.rs index c1cb80a1..d6b0b794 100644 --- a/crates/core/database/src/util/bridge/v0.rs +++ b/crates/core/database/src/util/bridge/v0.rs @@ -500,6 +500,17 @@ impl From for FieldsMember { } } +impl From for crate::FieldsMember { + fn from(value: FieldsMember) -> crate::FieldsMember { + match value { + FieldsMember::Avatar => crate::FieldsMember::Avatar, + FieldsMember::Nickname => crate::FieldsMember::Nickname, + FieldsMember::Roles => crate::FieldsMember::Roles, + FieldsMember::Timeout => crate::FieldsMember::Timeout, + } + } +} + impl From for RemovalIntention { fn from(value: crate::RemovalIntention) -> Self { match value { @@ -702,7 +713,8 @@ impl crate::User { } } - pub async fn into_known<'a, P>(self, perspective: P) -> User + /// Convert user object into user model assuming mutual connection + pub fn into_known<'a, P>(self, perspective: P, is_online: bool) -> User where P: Into>, { @@ -725,10 +737,7 @@ impl crate::User { }) .unwrap_or_default(); - // TODO: refactor all of this to be less code? (as in with the method above) - // TODO: also not sure if this is the best solution, but we don't want to - // TODO: fetch mutual information again here - let can_see_profile = relationship == RelationshipStatus::Friend; + let can_see_profile = relationship != RelationshipStatus::BlockedOther; (relationship, can_see_profile) } } else { @@ -768,7 +777,7 @@ impl crate::User { privileged: self.privileged, bot: self.bot.map(|bot| bot.into()), relationship, - online: can_see_profile && revolt_presence::is_online(&self.id).await, + online: can_see_profile && is_online, id: self.id, } } diff --git a/crates/core/models/src/v0/server_members.rs b/crates/core/models/src/v0/server_members.rs index 62b929b6..5b26b88a 100644 --- a/crates/core/models/src/v0/server_members.rs +++ b/crates/core/models/src/v0/server_members.rs @@ -1,9 +1,17 @@ -use super::File; +use std::collections::HashMap; + +use super::{File, Role, User}; use iso8601_timestamp::Timestamp; use once_cell::sync::Lazy; use regex::Regex; +#[cfg(feature = "validator")] +use validator::Validate; + +#[cfg(feature = "rocket")] +use rocket::FromForm; + /// Regex for valid role colours /// /// Allows the use of named colours, rgb(a), variables and all gradients. @@ -77,4 +85,46 @@ auto_derived!( Kick, Ban, } + + /// Member response + #[serde(untagged)] + pub enum MemberResponse { + Member(Member), + MemberWithRoles { + member: Member, + roles: HashMap, + }, + } + + /// Options for fetching all members + #[cfg_attr(feature = "rocket", derive(FromForm))] + pub struct OptionsFetchAllMembers { + /// Whether to exclude offline users + pub exclude_offline: Option, + } + + /// Response with all members + pub struct AllMemberResponse { + /// List of members + pub members: Vec, + /// List of users + pub users: Vec, + } + + /// New member information + #[cfg_attr(feature = "validator", derive(Validate))] + pub struct DataMemberEdit { + /// Member nickname + #[cfg_attr(feature = "validator", validate(length(min = 1, max = 32)))] + pub nickname: Option, + /// Attachment Id to set for avatar + pub avatar: Option, + /// Array of role ids + pub roles: Option>, + /// Timestamp this member is timed out until + pub timeout: Option, + /// Fields to remove from channel object + #[cfg_attr(feature = "validator", validate(length(min = 1)))] + pub remove: Option>, + } ); diff --git a/crates/core/models/src/v0/servers.rs b/crates/core/models/src/v0/servers.rs index 7a854175..424eb674 100644 --- a/crates/core/models/src/v0/servers.rs +++ b/crates/core/models/src/v0/servers.rs @@ -1,6 +1,6 @@ use super::{Channel, File}; -use revolt_permissions::OverrideField; +use revolt_permissions::{Override, OverrideField}; use std::collections::HashMap; #[cfg(feature = "validator")] @@ -160,6 +160,12 @@ auto_derived!( pub nsfw: Option, } + /// New role permissions + pub struct DataSetServerRolePermission { + /// Allow / deny values for the role in this server. + pub permissions: Override, + } + /// Information returned when creating server pub struct CreateServerLegacyResponse { /// Server object diff --git a/crates/delta/src/routes/channels/members_fetch.rs b/crates/delta/src/routes/channels/members_fetch.rs index 9b0cbf47..64a14a9b 100644 --- a/crates/delta/src/routes/channels/members_fetch.rs +++ b/crates/delta/src/routes/channels/members_fetch.rs @@ -28,13 +28,7 @@ pub async fn fetch_members( if let Channel::Group { recipients, .. } = channel { Ok(Json( - join_all( - db.fetch_users(&recipients) - .await? - .into_iter() - .map(|other_user| other_user.into_known(&user)), - ) - .await, + User::fetch_many_ids_as_mutuals(db, &user, &recipients).await?, )) } else { Err(create_error!(InvalidOperation)) diff --git a/crates/delta/src/routes/invites/invite_join.rs b/crates/delta/src/routes/invites/invite_join.rs index b558948e..b4e21ede 100644 --- a/crates/delta/src/routes/invites/invite_join.rs +++ b/crates/delta/src/routes/invites/invite_join.rs @@ -1,4 +1,3 @@ -use futures::future::join_all; use revolt_database::{util::reference::Reference, Channel, Database, Invite, Member, User}; use revolt_models::v0::{self, InviteJoinResponse}; use revolt_result::{create_error, Result}; @@ -37,13 +36,7 @@ pub async fn join( channel.add_user_to_group(db, &user, creator).await?; if let Channel::Group { recipients, .. } = &channel { Ok(Json(InviteJoinResponse::Group { - users: join_all( - db.fetch_users(&recipients) - .await? - .into_iter() - .map(|other_user| other_user.into_known(&user)), - ) - .await, + users: User::fetch_many_ids_as_mutuals(db, &user, &recipients).await?, channel: channel.into(), })) } else { diff --git a/crates/delta/src/routes/servers/member_edit.rs b/crates/delta/src/routes/servers/member_edit.rs index 07ffd8d1..8d478812 100644 --- a/crates/delta/src/routes/servers/member_edit.rs +++ b/crates/delta/src/routes/servers/member_edit.rs @@ -1,69 +1,55 @@ use std::collections::HashSet; -use revolt_quark::{ - models::{ - server_member::{FieldsMember, PartialMember}, - File, Member, User, - }, - perms, Db, Error, Permission, Ref, Result, Timestamp, +use revolt_database::{ + util::{permissions::DatabasePermissionQuery, reference::Reference}, + Database, File, PartialMember, User, }; +use revolt_models::v0; -use rocket::serde::json::Json; -use serde::{Deserialize, Serialize}; +use revolt_permissions::{calculate_server_permissions, ChannelPermission}; +use revolt_result::{create_error, Result}; +use rocket::{serde::json::Json, State}; use validator::Validate; -/// # Member Data -#[derive(Validate, Serialize, Deserialize, JsonSchema)] -pub struct DataMemberEdit { - /// Member nickname - #[validate(length(min = 1, max = 32))] - nickname: Option, - /// Attachment Id to set for avatar - avatar: Option, - /// Array of role ids - roles: Option>, - /// Timestamp this member is timed out until - timeout: Option, - /// Fields to remove from channel object - #[validate(length(min = 1))] - remove: Option>, -} - /// # Edit Member /// /// Edit a member by their id. #[openapi(tag = "Server Members")] #[patch("//members/", data = "")] -pub async fn req( - db: &Db, +pub async fn edit( + db: &State, user: User, - server: Ref, - target: Ref, - data: Json, -) -> Result> { + server: Reference, + target: Reference, + data: Json, +) -> Result> { let data = data.into_inner(); - data.validate() - .map_err(|error| Error::FailedValidation { error })?; + data.validate().map_err(|error| { + create_error!(FailedValidation { + error: error.to_string() + }) + })?; // Fetch server, target member and current permissions let mut server = server.as_server(db).await?; let mut member = target.as_member(db, &server.id).await?; - let mut permissions = perms(&user).server(&server); + let mut query = DatabasePermissionQuery::new(db, &user) + .server(&server) + .member(&member); + let permissions = calculate_server_permissions(&mut query).await; // Check permissions in server - let mut required = vec![]; - if data.nickname.is_some() || data .remove .as_ref() - .map(|x| x.contains(&FieldsMember::Nickname)) + .map(|x| x.contains(&v0::FieldsMember::Nickname)) .unwrap_or_default() { if user.id == member.id.user { - required.push(Permission::ChangeNickname); + permissions.throw_if_lacking_channel_permission(ChannelPermission::ChangeNickname)?; } else { - required.push(Permission::ManageNicknames); + permissions.throw_if_lacking_channel_permission(ChannelPermission::ManageNicknames)?; } } @@ -71,13 +57,13 @@ pub async fn req( || data .remove .as_ref() - .map(|x| x.contains(&FieldsMember::Avatar)) + .map(|x| x.contains(&v0::FieldsMember::Avatar)) .unwrap_or_default() { if user.id == member.id.user { - required.push(Permission::ChangeAvatar); + permissions.throw_if_lacking_channel_permission(ChannelPermission::ChangeAvatar)?; } else { - return Err(Error::InvalidOperation); + return Err(create_error!(InvalidOperation)); } } @@ -85,34 +71,30 @@ pub async fn req( || data .remove .as_ref() - .map(|x| x.contains(&FieldsMember::Roles)) + .map(|x| x.contains(&v0::FieldsMember::Roles)) .unwrap_or_default() { - required.push(Permission::AssignRoles); + permissions.throw_if_lacking_channel_permission(ChannelPermission::AssignRoles)?; } if data.timeout.is_some() || data .remove .as_ref() - .map(|x| x.contains(&FieldsMember::Timeout)) + .map(|x| x.contains(&v0::FieldsMember::Timeout)) .unwrap_or_default() { - required.push(Permission::TimeoutMembers); - } - - for permission in required { - permissions.throw_permission(db, permission).await?; + permissions.throw_if_lacking_channel_permission(ChannelPermission::TimeoutMembers)?; } // Resolve our ranking - let our_ranking = permissions.get_member_rank().unwrap_or(i64::MIN); + let our_ranking = query.get_member_rank().unwrap_or(i64::MIN); // Check that we have permissions to act against this member if member.id.user != user.id - && member.get_ranking(permissions.server.get().unwrap()) <= our_ranking + && member.get_ranking(query.server_ref().as_ref().unwrap()) <= our_ranking { - return Err(Error::NotElevated); + return Err(create_error!(NotElevated)); } // Check permissions against roles in diff @@ -125,16 +107,16 @@ pub async fn req( for role_id in added_roles { if let Some(role) = server.roles.remove(*role_id) { if role.rank <= our_ranking { - return Err(Error::NotElevated); + return Err(create_error!(NotElevated)); } } else { - return Err(Error::InvalidRole); + return Err(create_error!(InvalidRole)); } } } // Apply edits to the member object - let DataMemberEdit { + let v0::DataMemberEdit { nickname, avatar, roles, @@ -151,7 +133,7 @@ pub async fn req( // 1. Remove fields from object if let Some(fields) = &remove { - if fields.contains(&FieldsMember::Avatar) { + if fields.contains(&v0::FieldsMember::Avatar) { if let Some(avatar) = &member.avatar { db.mark_attachment_as_deleted(&avatar.id).await?; } @@ -164,8 +146,14 @@ pub async fn req( } member - .update(db, partial, remove.unwrap_or_default()) + .update( + db, + partial, + remove + .map(|v| v.into_iter().map(Into::into).collect()) + .unwrap_or_default(), + ) .await?; - Ok(Json(member)) + Ok(Json(member.into())) } diff --git a/crates/delta/src/routes/servers/member_experimental_query.rs b/crates/delta/src/routes/servers/member_experimental_query.rs index 44040ebe..125b8db9 100644 --- a/crates/delta/src/routes/servers/member_experimental_query.rs +++ b/crates/delta/src/routes/servers/member_experimental_query.rs @@ -1,9 +1,12 @@ -use revolt_quark::{ - models::{Member, User}, - perms, Db, Error, Ref, Result, +use futures::future::join_all; +use revolt_database::{ + util::{permissions::DatabasePermissionQuery, reference::Reference}, + Database, Member, User, }; - -use rocket::serde::json::Json; +use revolt_models::v0; +use revolt_permissions::PermissionQuery; +use revolt_result::{create_error, Result}; +use rocket::{serde::json::Json, State}; use serde::{Deserialize, Serialize}; /// # Query Parameters @@ -20,9 +23,9 @@ pub struct OptionsQueryMembers { #[derive(Serialize, JsonSchema)] pub struct MemberQueryResponse { /// List of members - members: Vec, + members: Vec, /// List of users - users: Vec, + users: Vec, } /// # Query members by name @@ -31,17 +34,20 @@ pub struct MemberQueryResponse { #[openapi(tag = "Server Members")] #[get("//members_experimental_query?")] pub async fn member_experimental_query( - db: &Db, + db: &State, user: User, - target: Ref, + target: Reference, options: OptionsQueryMembers, ) -> Result> { if !options.experimental_api { - return Err(Error::InternalError); + return Err(create_error!(InternalError)); } let server = target.as_server(db).await?; - perms(&user).server(&server).calc(db).await?; + let mut query = DatabasePermissionQuery::new(db, &user).server(&server); + if !query.are_we_a_member().await { + return Err(create_error!(NotFound)); + } let mut members = db.fetch_all_members(&server.id).await?; @@ -50,7 +56,7 @@ pub async fn member_experimental_query( user_ids.push(member.id.user.clone()); } - let mut users = User::fetch_foreign_users(db, &user_ids).await?; + let mut users = db.fetch_users(&user_ids).await?; // Ensure the lists match up exactly members.sort_by(|a, b| a.id.user.cmp(&b.id.user)); @@ -76,7 +82,15 @@ pub async fn member_experimental_query( left.len().cmp(&right.len()) }); - // Take the first five and return them - let (members, users) = zipped_vec.into_iter().take(10).unzip(); - Ok(Json(MemberQueryResponse { members, users })) + // Take the first ten and return them + let (members, users): (Vec, Vec) = zipped_vec.into_iter().take(10).unzip(); + Ok(Json(MemberQueryResponse { + members: members.into_iter().map(Into::into).collect(), + users: join_all( + users + .into_iter() + .map(|other_user| other_user.into(db, &user)), + ) + .await, + })) } diff --git a/crates/delta/src/routes/servers/member_fetch.rs b/crates/delta/src/routes/servers/member_fetch.rs index 2017ec48..b1f39740 100644 --- a/crates/delta/src/routes/servers/member_fetch.rs +++ b/crates/delta/src/routes/servers/member_fetch.rs @@ -1,25 +1,42 @@ -use revolt_quark::models::server_member::MemberResponse; -use revolt_quark::{models::User, perms, Db, Ref, Result}; -use rocket::serde::json::Json; +use revolt_database::{ + util::{permissions::DatabasePermissionQuery, reference::Reference}, + Database, User, +}; +use revolt_models::v0; +use revolt_permissions::PermissionQuery; +use revolt_result::{create_error, Result}; +use rocket::{serde::json::Json, State}; + /// # Fetch Member /// /// Retrieve a member. #[openapi(tag = "Server Members")] #[get("//members/?")] -pub async fn req( - db: &Db, +pub async fn fetch( + db: &State, user: User, - target: Ref, - member: Ref, + target: Reference, + member: Reference, roles: Option, -) -> Result> { +) -> Result> { let server = target.as_server(db).await?; - perms(&user).server(&server).calc(db).await?; + let mut query = DatabasePermissionQuery::new(db, &user).server(&server); + if !query.are_we_a_member().await { + return Err(create_error!(NotFound)); + } - let member_response: MemberResponse = match roles { - Some(true) => member.as_member_with_roles(db, &server.id).await?.into(), - _ => member.as_member(db, &server.id).await?.into(), - }; - - Ok(Json(member_response)) + let member = member.as_member(db, &server.id).await?; + if let Some(true) = roles { + Ok(Json(v0::MemberResponse::MemberWithRoles { + roles: server + .roles + .into_iter() + .filter(|(k, _)| member.roles.contains(k)) + .map(|(k, v)| (k, v.into())) + .collect(), + member: member.into(), + })) + } else { + Ok(Json(v0::MemberResponse::Member(member.into()))) + } } diff --git a/crates/delta/src/routes/servers/member_fetch_all.rs b/crates/delta/src/routes/servers/member_fetch_all.rs index 9ab8870b..29997668 100644 --- a/crates/delta/src/routes/servers/member_fetch_all.rs +++ b/crates/delta/src/routes/servers/member_fetch_all.rs @@ -1,51 +1,37 @@ -use revolt_quark::{ - models::{Member, User}, - perms, Db, Ref, Result, +use revolt_database::{ + util::{permissions::DatabasePermissionQuery, reference::Reference}, + Database, User, }; - -use rocket::serde::json::Json; -use serde::{Deserialize, Serialize}; - -/// # Query Parameters -#[derive(Deserialize, JsonSchema, FromForm)] -pub struct OptionsFetchAllMembers { - /// Whether to exclude offline users - exclude_offline: Option, -} - -/// # Member List -/// -/// Both lists are sorted by ID. -#[derive(Serialize, JsonSchema)] -pub struct AllMemberResponse { - /// List of members - members: Vec, - /// List of users - users: Vec, -} +use revolt_models::v0; +use revolt_permissions::PermissionQuery; +use revolt_result::{create_error, Result}; +use rocket::{serde::json::Json, State}; /// # Fetch Members /// /// Fetch all server members. #[openapi(tag = "Server Members")] #[get("//members?")] -pub async fn req( - db: &Db, +pub async fn fetch_all( + db: &State, user: User, - target: Ref, - options: OptionsFetchAllMembers, -) -> Result> { + target: Reference, + options: v0::OptionsFetchAllMembers, +) -> Result> { let server = target.as_server(db).await?; - perms(&user).server(&server).calc(db).await?; + let mut query = DatabasePermissionQuery::new(db, &user).server(&server); + if !query.are_we_a_member().await { + return Err(create_error!(NotFound)); + } let mut members = db.fetch_all_members(&server.id).await?; - let mut user_ids = vec![]; - for member in &members { - user_ids.push(member.id.user.clone()); - } + let user_ids: Vec = members + .iter() + .map(|member| member.id.user.clone()) + .collect(); - let mut users = User::fetch_foreign_users(db, &user_ids).await?; + let mut users = User::fetch_many_ids_as_mutuals(db, &user, &user_ids).await?; // Ensure the lists match up exactly. members.sort_by(|a, b| a.id.user.cmp(&b.id.user)); @@ -54,9 +40,12 @@ pub async fn req( // Optionally, remove all offline user entries. if let Some(true) = options.exclude_offline { let mut iter = users.iter(); - members.retain(|_| iter.next().unwrap().online.unwrap_or(false)); - users.retain(|user| user.online.unwrap_or(false)); + members.retain(|_| iter.next().unwrap().online); + users.retain(|user| user.online); } - Ok(Json(AllMemberResponse { members, users })) + Ok(Json(v0::AllMemberResponse { + members: members.into_iter().map(Into::into).collect(), + users, + })) } diff --git a/crates/delta/src/routes/servers/member_remove.rs b/crates/delta/src/routes/servers/member_remove.rs index 360d884e..a7e2f50a 100644 --- a/crates/delta/src/routes/servers/member_remove.rs +++ b/crates/delta/src/routes/servers/member_remove.rs @@ -1,40 +1,47 @@ -use revolt_quark::{ - models::{server_member::RemovalIntention, User}, - perms, Db, EmptyResponse, Error, Permission, Ref, Result, +use revolt_database::{ + util::{permissions::DatabasePermissionQuery, reference::Reference}, + Database, RemovalIntention, User, }; +use revolt_permissions::{calculate_server_permissions, ChannelPermission}; +use revolt_result::{create_error, Result}; +use rocket::State; +use rocket_empty::EmptyResponse; /// # Kick Member /// /// Removes a member from the server. #[openapi(tag = "Server Members")] #[delete("//members/")] -pub async fn req(db: &Db, user: User, target: Ref, member: Ref) -> Result { +pub async fn kick( + db: &State, + user: User, + target: Reference, + member: Reference, +) -> Result { let server = target.as_server(db).await?; if member.id == user.id { - return Err(Error::CannotRemoveYourself); + return Err(create_error!(CannotRemoveYourself)); } if member.id == server.owner { - return Err(Error::InvalidOperation); + return Err(create_error!(InvalidOperation)); } - let mut permissions = perms(&user).server(&server); - - permissions - .throw_permission(db, Permission::KickMembers) - .await?; + let mut query = DatabasePermissionQuery::new(db, &user).server(&server); + calculate_server_permissions(&mut query) + .await + .throw_if_lacking_channel_permission(ChannelPermission::KickMembers)?; let member = member.as_member(db, &server.id).await?; - - if member.get_ranking(permissions.server.get().unwrap()) - <= permissions.get_member_rank().unwrap_or(i64::MIN) + if member.get_ranking(query.server_ref().as_ref().unwrap()) + <= query.get_member_rank().unwrap_or(i64::MIN) { - return Err(Error::NotElevated); + return Err(create_error!(NotElevated)); } - server - .remove_member(db, member, RemovalIntention::Kick, false) + member + .remove(db, &server, RemovalIntention::Kick, false) .await .map(|_| EmptyResponse) } diff --git a/crates/delta/src/routes/servers/mod.rs b/crates/delta/src/routes/servers/mod.rs index 0b7f2bea..a0197130 100644 --- a/crates/delta/src/routes/servers/mod.rs +++ b/crates/delta/src/routes/servers/mod.rs @@ -32,10 +32,10 @@ pub fn routes() -> (Vec, OpenApi) { server_edit::req, server_ack::req, channel_create::create_server_channel, - member_fetch_all::req, - member_remove::req, - member_fetch::req, - member_edit::req, + member_fetch_all::fetch_all, + member_remove::kick, + member_fetch::fetch, + member_edit::edit, member_experimental_query::member_experimental_query, ban_create::ban, ban_remove::unban, @@ -45,8 +45,8 @@ pub fn routes() -> (Vec, OpenApi) { roles_edit::req, roles_fetch::req, roles_delete::req, - permissions_set::req, - permissions_set_default::req, + permissions_set::set_role_permission, + permissions_set_default::set_default_permissions, emoji_list::list_emoji ] } diff --git a/crates/delta/src/routes/servers/permissions_set.rs b/crates/delta/src/routes/servers/permissions_set.rs index 11939156..0c293aa8 100644 --- a/crates/delta/src/routes/servers/permissions_set.rs +++ b/crates/delta/src/routes/servers/permissions_set.rs @@ -1,58 +1,51 @@ -use rocket::serde::json::Json; -use serde::Deserialize; - -use revolt_quark::{ - models::{Server, User}, - perms, Db, Error, Override, Permission, Ref, Result, +use revolt_database::{ + util::{permissions::DatabasePermissionQuery, reference::Reference}, + Database, User, }; - -/// # Permission Value -#[derive(Deserialize, JsonSchema)] -pub struct DataSetServerRolePermission { - /// Allow / deny values for the role in this server. - permissions: Override, -} +use revolt_models::v0; +use revolt_permissions::{calculate_server_permissions, ChannelPermission, Override}; +use revolt_result::{create_error, Result}; +use rocket::{serde::json::Json, State}; /// # Set Role Permission /// /// Sets permissions for the specified role in the server. #[openapi(tag = "Server Permissions")] #[put("//permissions/", data = "", rank = 2)] -pub async fn req( - db: &Db, +pub async fn set_role_permission( + db: &State, user: User, - target: Ref, + target: Reference, role_id: String, - data: Json, -) -> Result> { + data: Json, +) -> Result> { let data = data.into_inner(); let mut server = target.as_server(db).await?; if let Some((current_value, rank)) = server.roles.get(&role_id).map(|x| (x.permissions, x.rank)) { - let mut permissions = perms(&user).server(&server); + let mut query = DatabasePermissionQuery::new(db, &user).server(&server); + let permissions = calculate_server_permissions(&mut query).await; - permissions - .throw_permission(db, Permission::ManagePermissions) - .await?; + permissions.throw_if_lacking_channel_permission(ChannelPermission::ManagePermissions)?; // Prevent us from editing roles above us - if rank <= permissions.get_member_rank().unwrap_or(i64::MIN) { - return Err(Error::NotElevated); + if rank <= query.get_member_rank().unwrap_or(i64::MIN) { + return Err(create_error!(NotElevated)); } // Ensure we have access to grant these permissions forwards let current_value: Override = current_value.into(); permissions - .throw_permission_override(db, current_value, data.permissions) + .throw_permission_override(current_value, &data.permissions) .await?; server .set_role_permission(db, &role_id, data.permissions.into()) .await?; - Ok(Json(server)) + Ok(Json(server.into())) } else { - Err(Error::NotFound) + Err(create_error!(NotFound)) } } diff --git a/crates/delta/src/routes/servers/permissions_set_default.rs b/crates/delta/src/routes/servers/permissions_set_default.rs index 477be513..8189e956 100644 --- a/crates/delta/src/routes/servers/permissions_set_default.rs +++ b/crates/delta/src/routes/servers/permissions_set_default.rs @@ -1,32 +1,42 @@ -use revolt_permissions::DataPermissionsValue; -use revolt_quark::{ - models::{server::PartialServer, Server, User}, - perms, Db, Permission, Ref, Result, +use revolt_database::{ + util::{permissions::DatabasePermissionQuery, reference::Reference}, + Database, PartialServer, Server, User, }; -use rocket::serde::json::Json; +use revolt_models::v0; +use revolt_permissions::{ + calculate_server_permissions, ChannelPermission, DataPermissionsValue, Override, +}; +use revolt_result::Result; +use rocket::{serde::json::Json, State}; + /// # Set Default Permission /// /// Sets permissions for the default role in this server. #[openapi(tag = "Server Permissions")] #[put("//permissions/default", data = "", rank = 1)] -pub async fn req( - db: &Db, +pub async fn set_default_permissions( + db: &State, user: User, - target: Ref, + target: Reference, data: Json, -) -> Result> { +) -> Result> { let data = data.into_inner(); let mut server = target.as_server(db).await?; - let mut permissions = perms(&user).server(&server); + let mut query = DatabasePermissionQuery::new(db, &user).server(&server); + let permissions = calculate_server_permissions(&mut query).await; - permissions - .throw_permission(db, Permission::ManagePermissions) - .await?; + permissions.throw_if_lacking_channel_permission(ChannelPermission::ManagePermissions)?; // Ensure we have permissions to grant these permissions forwards permissions - .throw_permission_value(db, data.permissions) + .throw_permission_override( + None, + &Override { + allow: data.permissions, + deny: 0, + }, + ) .await?; server @@ -40,5 +50,5 @@ pub async fn req( ) .await?; - Ok(Json(server)) + Ok(Json(server.into())) }