Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c020ef97ab | ||
|
|
e346464720 | ||
|
|
3ab5fda21d | ||
|
|
9e17a5e73a | ||
|
|
c7f09ab9d0 |
No files matched your search
@@ -200,6 +200,7 @@ message_reactions = 20
|
|||||||
server_emoji = 100
|
server_emoji = 100
|
||||||
server_roles = 200
|
server_roles = 200
|
||||||
server_channels = 200
|
server_channels = 200
|
||||||
|
server_categories = 50
|
||||||
|
|
||||||
# How many hours since creation a user is considered new
|
# How many hours since creation a user is considered new
|
||||||
new_user_hours = 72
|
new_user_hours = 72
|
||||||
|
|||||||
@@ -281,6 +281,7 @@ pub struct GlobalLimits {
|
|||||||
pub server_emoji: usize,
|
pub server_emoji: usize,
|
||||||
pub server_roles: usize,
|
pub server_roles: usize,
|
||||||
pub server_channels: usize,
|
pub server_channels: usize,
|
||||||
|
pub server_categories: usize,
|
||||||
|
|
||||||
pub new_user_hours: usize,
|
pub new_user_hours: usize,
|
||||||
|
|
||||||
|
|||||||
@@ -100,6 +100,10 @@ auto_derived!(
|
|||||||
)]
|
)]
|
||||||
role_permissions: HashMap<String, OverrideField>,
|
role_permissions: HashMap<String, OverrideField>,
|
||||||
|
|
||||||
|
/// Category the channel is in
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
parent: Option<String>,
|
||||||
|
|
||||||
/// Whether this channel is marked as not safe for work
|
/// Whether this channel is marked as not safe for work
|
||||||
#[serde(skip_serializing_if = "crate::if_false", default)]
|
#[serde(skip_serializing_if = "crate::if_false", default)]
|
||||||
nsfw: bool,
|
nsfw: bool,
|
||||||
@@ -131,6 +135,10 @@ auto_derived!(
|
|||||||
)]
|
)]
|
||||||
role_permissions: HashMap<String, OverrideField>,
|
role_permissions: HashMap<String, OverrideField>,
|
||||||
|
|
||||||
|
/// Category the channel is in
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
parent: Option<String>,
|
||||||
|
|
||||||
/// Whether this channel is marked as not safe for work
|
/// Whether this channel is marked as not safe for work
|
||||||
#[serde(skip_serializing_if = "crate::if_false", default)]
|
#[serde(skip_serializing_if = "crate::if_false", default)]
|
||||||
nsfw: bool,
|
nsfw: bool,
|
||||||
@@ -161,6 +169,8 @@ auto_derived!(
|
|||||||
pub default_permissions: Option<OverrideField>,
|
pub default_permissions: Option<OverrideField>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub last_message_id: Option<String>,
|
pub last_message_id: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub parent: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Optional fields on channel object
|
/// Optional fields on channel object
|
||||||
@@ -219,6 +229,7 @@ impl Channel {
|
|||||||
default_permissions: None,
|
default_permissions: None,
|
||||||
role_permissions: HashMap::new(),
|
role_permissions: HashMap::new(),
|
||||||
nsfw: data.nsfw.unwrap_or(false),
|
nsfw: data.nsfw.unwrap_or(false),
|
||||||
|
parent: None
|
||||||
},
|
},
|
||||||
v0::LegacyServerChannelType::Voice => Channel::VoiceChannel {
|
v0::LegacyServerChannelType::Voice => Channel::VoiceChannel {
|
||||||
id: id.clone(),
|
id: id.clone(),
|
||||||
@@ -229,6 +240,7 @@ impl Channel {
|
|||||||
default_permissions: None,
|
default_permissions: None,
|
||||||
role_permissions: HashMap::new(),
|
role_permissions: HashMap::new(),
|
||||||
nsfw: data.nsfw.unwrap_or(false),
|
nsfw: data.nsfw.unwrap_or(false),
|
||||||
|
parent: None
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -437,6 +449,13 @@ impl Channel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn parent(&self) -> Option<&str> {
|
||||||
|
match self {
|
||||||
|
Channel::TextChannel { parent, .. } | Channel::VoiceChannel { parent, .. } => parent.as_deref(),
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Set role permission on a channel
|
/// Set role permission on a channel
|
||||||
pub async fn set_role_permission(
|
pub async fn set_role_permission(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
use revolt_models::v0::{self, DataCreateServerChannel};
|
use revolt_models::v0::{self, DataCreateCategory, DataCreateServerChannel, DataEditCategory};
|
||||||
use revolt_permissions::{OverrideField, DEFAULT_PERMISSION_SERVER};
|
use revolt_permissions::{OverrideField, DEFAULT_PERMISSION_SERVER};
|
||||||
use revolt_result::Result;
|
use revolt_result::Result;
|
||||||
use ulid::Ulid;
|
use ulid::Ulid;
|
||||||
@@ -26,8 +26,8 @@ auto_derived_partial!(
|
|||||||
// TODO: investigate if this is redundant and can be removed
|
// TODO: investigate if this is redundant and can be removed
|
||||||
pub channels: Vec<String>,
|
pub channels: Vec<String>,
|
||||||
/// Categories for this server
|
/// Categories for this server
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "HashMap::is_empty")]
|
||||||
pub categories: Option<Vec<Category>>,
|
pub categories: HashMap<String, Category>,
|
||||||
/// Configuration for sending system event messages
|
/// Configuration for sending system event messages
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub system_messages: Option<SystemMessageChannels>,
|
pub system_messages: Option<SystemMessageChannels>,
|
||||||
@@ -87,17 +87,29 @@ auto_derived_partial!(
|
|||||||
"PartialRole"
|
"PartialRole"
|
||||||
);
|
);
|
||||||
|
|
||||||
auto_derived!(
|
auto_derived_partial!(
|
||||||
/// Channel category
|
/// Channel category
|
||||||
pub struct Category {
|
pub struct Category {
|
||||||
/// Unique ID for this category
|
/// Unique ID for this category
|
||||||
pub id: String,
|
pub id: String,
|
||||||
/// Title for this category
|
/// Title for this category
|
||||||
pub title: String,
|
pub title: String,
|
||||||
|
/// Default permissions assigned to users in this category
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub default_permissions: Option<OverrideField>,
|
||||||
|
/// Permissions assigned based on role to this category
|
||||||
|
#[serde(
|
||||||
|
default = "HashMap::<String, OverrideField>::new",
|
||||||
|
skip_serializing_if = "HashMap::<String, OverrideField>::is_empty"
|
||||||
|
)]
|
||||||
|
pub role_permissions: HashMap<String, OverrideField>,
|
||||||
/// Channels in this category
|
/// Channels in this category
|
||||||
pub channels: Vec<String>,
|
pub channels: Vec<String>,
|
||||||
}
|
},
|
||||||
|
"PartialCategory"
|
||||||
|
);
|
||||||
|
|
||||||
|
auto_derived!(
|
||||||
/// System message channel assignments
|
/// System message channel assignments
|
||||||
pub struct SystemMessageChannels {
|
pub struct SystemMessageChannels {
|
||||||
/// ID of channel to send user join messages in
|
/// ID of channel to send user join messages in
|
||||||
@@ -127,6 +139,10 @@ auto_derived!(
|
|||||||
pub enum FieldsRole {
|
pub enum FieldsRole {
|
||||||
Colour,
|
Colour,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum FieldsCategory {
|
||||||
|
DefaultPermissions
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
#[allow(clippy::disallowed_methods)]
|
#[allow(clippy::disallowed_methods)]
|
||||||
@@ -149,7 +165,7 @@ impl Server {
|
|||||||
|
|
||||||
analytics: false,
|
analytics: false,
|
||||||
banner: None,
|
banner: None,
|
||||||
categories: None,
|
categories: HashMap::new(),
|
||||||
discoverable: false,
|
discoverable: false,
|
||||||
flags: None,
|
flags: None,
|
||||||
icon: None,
|
icon: None,
|
||||||
@@ -221,7 +237,7 @@ impl Server {
|
|||||||
pub fn remove_field(&mut self, field: &FieldsServer) {
|
pub fn remove_field(&mut self, field: &FieldsServer) {
|
||||||
match field {
|
match field {
|
||||||
FieldsServer::Description => self.description = None,
|
FieldsServer::Description => self.description = None,
|
||||||
FieldsServer::Categories => self.categories = None,
|
FieldsServer::Categories => self.categories.clear(),
|
||||||
FieldsServer::SystemMessages => self.system_messages = None,
|
FieldsServer::SystemMessages => self.system_messages = None,
|
||||||
FieldsServer::Icon => self.icon = None,
|
FieldsServer::Icon => self.icon = None,
|
||||||
FieldsServer::Banner => self.banner = None,
|
FieldsServer::Banner => self.banner = None,
|
||||||
@@ -334,6 +350,110 @@ impl Role {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Category {
|
||||||
|
pub fn remove_field(&mut self, field: FieldsCategory) {
|
||||||
|
match field {
|
||||||
|
FieldsCategory::DefaultPermissions => self.default_permissions = None
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn create(db: &Database, server: &mut Server, data: DataCreateCategory) -> Result<Category> {
|
||||||
|
let channels = data.channels.clone().unwrap_or_default()
|
||||||
|
.into_iter()
|
||||||
|
.filter(|c| server.channels.contains(&c))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let category = Category {
|
||||||
|
id: Ulid::new().to_string(),
|
||||||
|
title: data.title,
|
||||||
|
channels: channels,
|
||||||
|
default_permissions: None,
|
||||||
|
role_permissions: HashMap::new()
|
||||||
|
};
|
||||||
|
|
||||||
|
server.categories.insert(category.id.clone(), category.clone());
|
||||||
|
|
||||||
|
let partial_server = PartialServer { categories: Some(server.categories.clone()), ..Default::default() };
|
||||||
|
|
||||||
|
db.update_server(&server.id, &partial_server, Vec::new()).await?;
|
||||||
|
|
||||||
|
EventV1::ServerUpdate {
|
||||||
|
id: server.id.clone(),
|
||||||
|
data: partial_server.into(),
|
||||||
|
clear: Vec::new(),
|
||||||
|
}
|
||||||
|
.p(server.id.clone())
|
||||||
|
.await;
|
||||||
|
|
||||||
|
Ok(category)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn delete(&self, db: &Database, server: &mut Server) -> Result<()> {
|
||||||
|
// update the parent server model with the new category
|
||||||
|
server.categories.remove(&self.id);
|
||||||
|
|
||||||
|
let partial_server = PartialServer { categories: Some(server.categories.clone()), ..Default::default() };
|
||||||
|
|
||||||
|
db.update_server(&server.id, &partial_server, Vec::new()).await?;
|
||||||
|
|
||||||
|
EventV1::ServerUpdate {
|
||||||
|
id: server.id.clone(),
|
||||||
|
data: partial_server.into(),
|
||||||
|
clear: Vec::new(),
|
||||||
|
}
|
||||||
|
.p(server.id.clone())
|
||||||
|
.await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn update(&mut self, db: &Database, server: &mut Server, partial: PartialCategory, remove: Vec<FieldsCategory>) -> Result<()> {
|
||||||
|
for field in remove {
|
||||||
|
self.remove_field(field);
|
||||||
|
};
|
||||||
|
|
||||||
|
self.apply_options(partial);
|
||||||
|
|
||||||
|
// update the parent server model with the new category
|
||||||
|
server.categories.insert(self.id.clone(), self.clone());
|
||||||
|
|
||||||
|
let partial_server = PartialServer { categories: Some(server.categories.clone()), ..Default::default() };
|
||||||
|
|
||||||
|
db.update_server(&server.id, &partial_server, Vec::new()).await?;
|
||||||
|
|
||||||
|
EventV1::ServerUpdate {
|
||||||
|
id: server.id.clone(),
|
||||||
|
data: partial_server.into(),
|
||||||
|
clear: Vec::new(),
|
||||||
|
}
|
||||||
|
.p(server.id.clone())
|
||||||
|
.await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn set_role_permission(&mut self, db: &Database, server: &mut Server, role_id: String, role_override: OverrideField) -> Result<()> {
|
||||||
|
self.role_permissions.insert(role_id, role_override);
|
||||||
|
|
||||||
|
// update the parent server model with the new category
|
||||||
|
server.categories.insert(self.id.clone(), self.clone());
|
||||||
|
|
||||||
|
let partial_server = PartialServer { categories: Some(server.categories.clone()), ..Default::default() };
|
||||||
|
|
||||||
|
db.update_server(&server.id, &partial_server, Vec::new()).await?;
|
||||||
|
|
||||||
|
EventV1::ServerUpdate {
|
||||||
|
id: server.id.clone(),
|
||||||
|
data: partial_server.into(),
|
||||||
|
clear: Vec::new(),
|
||||||
|
}
|
||||||
|
.p(server.id.clone())
|
||||||
|
.await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl SystemMessageChannels {
|
impl SystemMessageChannels {
|
||||||
pub fn into_channel_ids(self) -> HashSet<String> {
|
pub fn into_channel_ids(self) -> HashSet<String> {
|
||||||
let mut ids = HashSet::new();
|
let mut ids = HashSet::new();
|
||||||
|
|||||||
@@ -187,6 +187,7 @@ impl From<crate::Channel> for Channel {
|
|||||||
last_message_id,
|
last_message_id,
|
||||||
default_permissions,
|
default_permissions,
|
||||||
role_permissions,
|
role_permissions,
|
||||||
|
parent,
|
||||||
nsfw,
|
nsfw,
|
||||||
} => Channel::TextChannel {
|
} => Channel::TextChannel {
|
||||||
id,
|
id,
|
||||||
@@ -197,6 +198,7 @@ impl From<crate::Channel> for Channel {
|
|||||||
last_message_id,
|
last_message_id,
|
||||||
default_permissions,
|
default_permissions,
|
||||||
role_permissions,
|
role_permissions,
|
||||||
|
parent,
|
||||||
nsfw,
|
nsfw,
|
||||||
},
|
},
|
||||||
crate::Channel::VoiceChannel {
|
crate::Channel::VoiceChannel {
|
||||||
@@ -207,6 +209,7 @@ impl From<crate::Channel> for Channel {
|
|||||||
icon,
|
icon,
|
||||||
default_permissions,
|
default_permissions,
|
||||||
role_permissions,
|
role_permissions,
|
||||||
|
parent,
|
||||||
nsfw,
|
nsfw,
|
||||||
} => Channel::VoiceChannel {
|
} => Channel::VoiceChannel {
|
||||||
id,
|
id,
|
||||||
@@ -216,6 +219,7 @@ impl From<crate::Channel> for Channel {
|
|||||||
icon: icon.map(|file| file.into()),
|
icon: icon.map(|file| file.into()),
|
||||||
default_permissions,
|
default_permissions,
|
||||||
role_permissions,
|
role_permissions,
|
||||||
|
parent,
|
||||||
nsfw,
|
nsfw,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -267,6 +271,7 @@ impl From<Channel> for crate::Channel {
|
|||||||
last_message_id,
|
last_message_id,
|
||||||
default_permissions,
|
default_permissions,
|
||||||
role_permissions,
|
role_permissions,
|
||||||
|
parent,
|
||||||
nsfw,
|
nsfw,
|
||||||
} => crate::Channel::TextChannel {
|
} => crate::Channel::TextChannel {
|
||||||
id,
|
id,
|
||||||
@@ -277,6 +282,7 @@ impl From<Channel> for crate::Channel {
|
|||||||
last_message_id,
|
last_message_id,
|
||||||
default_permissions,
|
default_permissions,
|
||||||
role_permissions,
|
role_permissions,
|
||||||
|
parent,
|
||||||
nsfw,
|
nsfw,
|
||||||
},
|
},
|
||||||
Channel::VoiceChannel {
|
Channel::VoiceChannel {
|
||||||
@@ -287,6 +293,7 @@ impl From<Channel> for crate::Channel {
|
|||||||
icon,
|
icon,
|
||||||
default_permissions,
|
default_permissions,
|
||||||
role_permissions,
|
role_permissions,
|
||||||
|
parent,
|
||||||
nsfw,
|
nsfw,
|
||||||
} => crate::Channel::VoiceChannel {
|
} => crate::Channel::VoiceChannel {
|
||||||
id,
|
id,
|
||||||
@@ -296,6 +303,7 @@ impl From<Channel> for crate::Channel {
|
|||||||
icon: icon.map(|file| file.into()),
|
icon: icon.map(|file| file.into()),
|
||||||
default_permissions,
|
default_permissions,
|
||||||
role_permissions,
|
role_permissions,
|
||||||
|
parent,
|
||||||
nsfw,
|
nsfw,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -315,6 +323,7 @@ impl From<crate::PartialChannel> for PartialChannel {
|
|||||||
role_permissions: value.role_permissions,
|
role_permissions: value.role_permissions,
|
||||||
default_permissions: value.default_permissions,
|
default_permissions: value.default_permissions,
|
||||||
last_message_id: value.last_message_id,
|
last_message_id: value.last_message_id,
|
||||||
|
parent: value.parent,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -332,6 +341,7 @@ impl From<PartialChannel> for crate::PartialChannel {
|
|||||||
role_permissions: value.role_permissions,
|
role_permissions: value.role_permissions,
|
||||||
default_permissions: value.default_permissions,
|
default_permissions: value.default_permissions,
|
||||||
last_message_id: value.last_message_id,
|
last_message_id: value.last_message_id,
|
||||||
|
parent: value.parent,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -729,9 +739,7 @@ impl From<crate::Server> for Server {
|
|||||||
name: value.name,
|
name: value.name,
|
||||||
description: value.description,
|
description: value.description,
|
||||||
channels: value.channels,
|
channels: value.channels,
|
||||||
categories: value
|
categories: value.categories.into_iter().map(|(k, v)| (k, v.into())).collect(),
|
||||||
.categories
|
|
||||||
.map(|categories| categories.into_iter().map(|v| v.into()).collect()),
|
|
||||||
system_messages: value.system_messages.map(|v| v.into()),
|
system_messages: value.system_messages.map(|v| v.into()),
|
||||||
roles: value
|
roles: value
|
||||||
.roles
|
.roles
|
||||||
@@ -757,9 +765,7 @@ impl From<Server> for crate::Server {
|
|||||||
name: value.name,
|
name: value.name,
|
||||||
description: value.description,
|
description: value.description,
|
||||||
channels: value.channels,
|
channels: value.channels,
|
||||||
categories: value
|
categories: value.categories.into_iter().map(|(k, v)| (k, v.into())).collect(),
|
||||||
.categories
|
|
||||||
.map(|categories| categories.into_iter().map(|v| v.into()).collect()),
|
|
||||||
system_messages: value.system_messages.map(|v| v.into()),
|
system_messages: value.system_messages.map(|v| v.into()),
|
||||||
roles: value
|
roles: value
|
||||||
.roles
|
.roles
|
||||||
@@ -785,9 +791,7 @@ impl From<crate::PartialServer> for PartialServer {
|
|||||||
name: value.name,
|
name: value.name,
|
||||||
description: value.description,
|
description: value.description,
|
||||||
channels: value.channels,
|
channels: value.channels,
|
||||||
categories: value
|
categories: value.categories.map(|x| x.into_iter().map(|(k, v)| (k, v.into())).collect()),
|
||||||
.categories
|
|
||||||
.map(|categories| categories.into_iter().map(|v| v.into()).collect()),
|
|
||||||
system_messages: value.system_messages.map(|v| v.into()),
|
system_messages: value.system_messages.map(|v| v.into()),
|
||||||
roles: value
|
roles: value
|
||||||
.roles
|
.roles
|
||||||
@@ -811,9 +815,7 @@ impl From<PartialServer> for crate::PartialServer {
|
|||||||
name: value.name,
|
name: value.name,
|
||||||
description: value.description,
|
description: value.description,
|
||||||
channels: value.channels,
|
channels: value.channels,
|
||||||
categories: value
|
categories: value.categories.map(|x| x.into_iter().map(|(k, v)| (k, v.into())).collect()),
|
||||||
.categories
|
|
||||||
.map(|categories| categories.into_iter().map(|v| v.into()).collect()),
|
|
||||||
system_messages: value.system_messages.map(|v| v.into()),
|
system_messages: value.system_messages.map(|v| v.into()),
|
||||||
roles: value
|
roles: value
|
||||||
.roles
|
.roles
|
||||||
@@ -859,6 +861,8 @@ impl From<crate::Category> for Category {
|
|||||||
id: value.id,
|
id: value.id,
|
||||||
title: value.title,
|
title: value.title,
|
||||||
channels: value.channels,
|
channels: value.channels,
|
||||||
|
role_permissions: value.role_permissions,
|
||||||
|
default_permissions: value.default_permissions,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -869,6 +873,24 @@ impl From<Category> for crate::Category {
|
|||||||
id: value.id,
|
id: value.id,
|
||||||
title: value.title,
|
title: value.title,
|
||||||
channels: value.channels,
|
channels: value.channels,
|
||||||
|
role_permissions: value.role_permissions,
|
||||||
|
default_permissions: value.default_permissions,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::FieldsCategory> for FieldsCategory {
|
||||||
|
fn from(value: crate::FieldsCategory) -> Self {
|
||||||
|
match value {
|
||||||
|
crate::FieldsCategory::DefaultPermissions => FieldsCategory::DefaultPermissions,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<FieldsCategory> for crate::FieldsCategory {
|
||||||
|
fn from(value: FieldsCategory) -> crate::FieldsCategory {
|
||||||
|
match value {
|
||||||
|
FieldsCategory::DefaultPermissions => crate::FieldsCategory::DefaultPermissions,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use revolt_permissions::{
|
|||||||
DEFAULT_PERMISSION_DIRECT_MESSAGE,
|
DEFAULT_PERMISSION_DIRECT_MESSAGE,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{Channel, Database, Member, Server, User};
|
use crate::{Category, Channel, Database, Member, Server, User};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct BulkDatabasePermissionQuery<'a> {
|
pub struct BulkDatabasePermissionQuery<'a> {
|
||||||
@@ -197,7 +197,7 @@ async fn calculate_members_permissions<'a>(
|
|||||||
) -> HashMap<String, PermissionValue> {
|
) -> HashMap<String, PermissionValue> {
|
||||||
let mut resp = HashMap::new();
|
let mut resp = HashMap::new();
|
||||||
|
|
||||||
let (_, channel_role_permissions, channel_default_permissions) = match query
|
let (_, channel_role_permissions, channel_default_permissions, parent) = match query
|
||||||
.channel
|
.channel
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.expect("A channel must be assigned to calculate channel permissions")
|
.expect("A channel must be assigned to calculate channel permissions")
|
||||||
@@ -207,14 +207,16 @@ async fn calculate_members_permissions<'a>(
|
|||||||
id,
|
id,
|
||||||
role_permissions,
|
role_permissions,
|
||||||
default_permissions,
|
default_permissions,
|
||||||
|
parent,
|
||||||
..
|
..
|
||||||
}
|
}
|
||||||
| Channel::VoiceChannel {
|
| Channel::VoiceChannel {
|
||||||
id,
|
id,
|
||||||
role_permissions,
|
role_permissions,
|
||||||
default_permissions,
|
default_permissions,
|
||||||
|
parent,
|
||||||
..
|
..
|
||||||
} => (id, role_permissions, default_permissions),
|
} => (id, role_permissions, default_permissions, parent),
|
||||||
_ => panic!("Calculation of member permissions must be done on a server channel"),
|
_ => panic!("Calculation of member permissions must be done on a server channel"),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -268,6 +270,8 @@ async fn calculate_members_permissions<'a>(
|
|||||||
.map(|m| (&m.id.user, m)),
|
.map(|m| (&m.id.user, m)),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let category = parent.map(|parent| query.server.categories.get(&parent).expect("Channel parent doesnt exist"));
|
||||||
|
|
||||||
for user in users {
|
for user in users {
|
||||||
let member = members.get(&user.id);
|
let member = members.get(&user.id);
|
||||||
|
|
||||||
@@ -298,11 +302,39 @@ async fn calculate_members_permissions<'a>(
|
|||||||
// Get the user's server permissions
|
// Get the user's server permissions
|
||||||
let mut permission = calculate_server_permissions(&query.server, user, member);
|
let mut permission = calculate_server_permissions(&query.server, user, member);
|
||||||
|
|
||||||
|
// Apply category overrides
|
||||||
|
if let Some(category) = category {
|
||||||
|
if let Some(defaults) = category.default_permissions {
|
||||||
|
permission.apply(defaults.into());
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get the applicable role overrides for the category
|
||||||
|
let mut roles = category.role_permissions
|
||||||
|
.iter()
|
||||||
|
.filter(|(id, _)| member.roles.contains(id))
|
||||||
|
.filter_map(|(id, permission)| {
|
||||||
|
query.server.roles.get(id).map(|role| {
|
||||||
|
let v: Override = (*permission).into();
|
||||||
|
(role.rank, v)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<Vec<(i64, Override)>>();
|
||||||
|
|
||||||
|
roles.sort_by(|a, b| b.0.cmp(&a.0));
|
||||||
|
let overrides = roles.into_iter().map(|(_, v)| v);
|
||||||
|
|
||||||
|
for role_override in overrides {
|
||||||
|
permission.apply(role_override)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// apply channel overrides
|
||||||
|
|
||||||
if let Some(defaults) = channel_default_permissions {
|
if let Some(defaults) = channel_default_permissions {
|
||||||
permission.apply(defaults.into());
|
permission.apply(defaults.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the applicable role overrides
|
// Get the applicable role overrides for the channel
|
||||||
let mut roles = channel_role_permissions
|
let mut roles = channel_role_permissions
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(id, _)| member.roles.contains(id))
|
.filter(|(id, _)| member.roles.contains(id))
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use revolt_permissions::{
|
|||||||
RelationshipStatus, DEFAULT_PERMISSION_DIRECT_MESSAGE,
|
RelationshipStatus, DEFAULT_PERMISSION_DIRECT_MESSAGE,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{Channel, Database, Member, Server, User};
|
use crate::{Category, Channel, Database, Member, Server, User};
|
||||||
|
|
||||||
/// Permissions calculator
|
/// Permissions calculator
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -16,6 +16,7 @@ pub struct DatabasePermissionQuery<'a> {
|
|||||||
perspective: &'a User,
|
perspective: &'a User,
|
||||||
user: Option<Cow<'a, User>>,
|
user: Option<Cow<'a, User>>,
|
||||||
channel: Option<Cow<'a, Channel>>,
|
channel: Option<Cow<'a, Channel>>,
|
||||||
|
category: Option<Cow<'a, Category>>,
|
||||||
server: Option<Cow<'a, Server>>,
|
server: Option<Cow<'a, Server>>,
|
||||||
member: Option<Cow<'a, Member>>,
|
member: Option<Cow<'a, Member>>,
|
||||||
|
|
||||||
@@ -288,6 +289,40 @@ impl PermissionQuery for DatabasePermissionQuery<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_default_category_permissions(&mut self) -> Override {
|
||||||
|
if let Some(category) = &self.category {
|
||||||
|
category.default_permissions.unwrap_or_default().into()
|
||||||
|
} else {
|
||||||
|
Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_our_category_role_overrides(&mut self) -> Vec<Override> {
|
||||||
|
if let Some((server, category)) = self.server.as_ref().zip(self.category.as_ref()) {
|
||||||
|
let member_roles = self
|
||||||
|
.member
|
||||||
|
.as_ref()
|
||||||
|
.map(|member| member.roles.clone())
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
let mut roles = category.role_permissions
|
||||||
|
.iter()
|
||||||
|
.filter(|(id, _)| member_roles.contains(id))
|
||||||
|
.filter_map(|(id, permission)| {
|
||||||
|
server.roles.get(id).map(|role| {
|
||||||
|
let v: Override = (*permission).into();
|
||||||
|
(role.rank, v)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<Vec<(i64, Override)>>();
|
||||||
|
|
||||||
|
roles.sort_by(|a, b| b.0.cmp(&a.0));
|
||||||
|
roles.into_iter().map(|(_, v)| v).collect()
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Do we own this group or saved messages channel if it is one of those?
|
/// Do we own this group or saved messages channel if it is one of those?
|
||||||
async fn do_we_own_the_channel(&mut self) -> bool {
|
async fn do_we_own_the_channel(&mut self) -> bool {
|
||||||
if let Some(channel) = &self.channel {
|
if let Some(channel) = &self.channel {
|
||||||
@@ -375,6 +410,14 @@ impl PermissionQuery for DatabasePermissionQuery<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn set_category_from_channel(&mut self) {
|
||||||
|
if let Some((server, channel)) = self.server.as_ref().zip(self.channel.as_ref()) {
|
||||||
|
let category = channel.parent().and_then(|id| server.categories.get(id)).cloned();
|
||||||
|
|
||||||
|
self.category = category.map(Cow::Owned);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> DatabasePermissionQuery<'a> {
|
impl<'a> DatabasePermissionQuery<'a> {
|
||||||
@@ -385,6 +428,7 @@ impl<'a> DatabasePermissionQuery<'a> {
|
|||||||
perspective,
|
perspective,
|
||||||
user: None,
|
user: None,
|
||||||
channel: None,
|
channel: None,
|
||||||
|
category: None,
|
||||||
server: None,
|
server: None,
|
||||||
member: None,
|
member: None,
|
||||||
|
|
||||||
@@ -443,6 +487,13 @@ impl<'a> DatabasePermissionQuery<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn category(self, category: &'a Category) -> DatabasePermissionQuery<'a> {
|
||||||
|
DatabasePermissionQuery {
|
||||||
|
category: Some(Cow::Borrowed(category)),
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Use member
|
/// Use member
|
||||||
pub fn member(self, member: &'a Member) -> DatabasePermissionQuery {
|
pub fn member(self, member: &'a Member) -> DatabasePermissionQuery {
|
||||||
DatabasePermissionQuery {
|
DatabasePermissionQuery {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use schemars::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Bot, Channel, Database, Emoji, Invite, Member, Message, Server, ServerBan, User, Webhook,
|
Bot, Category, Channel, Database, Emoji, Invite, Member, Message, Server, ServerBan, User, Webhook
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Reference to some object in the database
|
/// Reference to some object in the database
|
||||||
|
|||||||
@@ -101,6 +101,10 @@ auto_derived!(
|
|||||||
)]
|
)]
|
||||||
role_permissions: HashMap<String, OverrideField>,
|
role_permissions: HashMap<String, OverrideField>,
|
||||||
|
|
||||||
|
/// Category the channel is in
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
parent: Option<String>,
|
||||||
|
|
||||||
/// Whether this channel is marked as not safe for work
|
/// Whether this channel is marked as not safe for work
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "serde",
|
feature = "serde",
|
||||||
@@ -138,6 +142,10 @@ auto_derived!(
|
|||||||
)]
|
)]
|
||||||
role_permissions: HashMap<String, OverrideField>,
|
role_permissions: HashMap<String, OverrideField>,
|
||||||
|
|
||||||
|
/// Category the channel is in
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
parent: Option<String>,
|
||||||
|
|
||||||
/// Whether this channel is marked as not safe for work
|
/// Whether this channel is marked as not safe for work
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "serde",
|
feature = "serde",
|
||||||
@@ -170,6 +178,8 @@ auto_derived!(
|
|||||||
pub default_permissions: Option<OverrideField>,
|
pub default_permissions: Option<OverrideField>,
|
||||||
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
pub last_message_id: Option<String>,
|
pub last_message_id: Option<String>,
|
||||||
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
|
pub parent: Option<String>
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Optional fields on channel object
|
/// Optional fields on channel object
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ auto_derived_partial!(
|
|||||||
// TODO: investigate if this is redundant and can be removed
|
// TODO: investigate if this is redundant and can be removed
|
||||||
pub channels: Vec<String>,
|
pub channels: Vec<String>,
|
||||||
/// Categories for this server
|
/// Categories for this server
|
||||||
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "HashMap::is_empty"))]
|
||||||
pub categories: Option<Vec<Category>>,
|
pub categories: HashMap<String, Category>,
|
||||||
/// Configuration for sending system event messages
|
/// Configuration for sending system event messages
|
||||||
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
pub system_messages: Option<SystemMessageChannels>,
|
pub system_messages: Option<SystemMessageChannels>,
|
||||||
@@ -126,15 +126,52 @@ auto_derived!(
|
|||||||
#[cfg_attr(feature = "validator", derive(Validate))]
|
#[cfg_attr(feature = "validator", derive(Validate))]
|
||||||
pub struct Category {
|
pub struct Category {
|
||||||
/// Unique ID for this category
|
/// Unique ID for this category
|
||||||
#[cfg_attr(feature = "validator", validate(length(min = 1, max = 32)))]
|
|
||||||
pub id: String,
|
pub id: String,
|
||||||
/// Title for this category
|
/// Title for this category
|
||||||
#[cfg_attr(feature = "validator", validate(length(min = 1, max = 32)))]
|
#[cfg_attr(feature = "validator", validate(length(min = 1, max = 32)))]
|
||||||
pub title: String,
|
pub title: String,
|
||||||
|
/// Default permissions assigned to users in this channel
|
||||||
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
|
pub default_permissions: Option<OverrideField>,
|
||||||
|
/// Permissions assigned based on role to this channel
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
serde(
|
||||||
|
default = "HashMap::<String, OverrideField>::new",
|
||||||
|
skip_serializing_if = "HashMap::<String, OverrideField>::is_empty"
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
pub role_permissions: HashMap<String, OverrideField>,
|
||||||
/// Channels in this category
|
/// Channels in this category
|
||||||
pub channels: Vec<String>,
|
pub channels: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "validator", derive(Validate))]
|
||||||
|
pub struct DataCreateCategory {
|
||||||
|
#[cfg_attr(feature = "validator", validate(length(min = 1, max = 32)))]
|
||||||
|
pub title: String,
|
||||||
|
/// Channels in this category
|
||||||
|
pub channels: Option<Vec<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "validator", derive(Validate))]
|
||||||
|
pub struct DataEditCategory {
|
||||||
|
#[cfg_attr(feature = "validator", validate(length(min = 1, max = 32)))]
|
||||||
|
pub title: Option<String>,
|
||||||
|
/// Channels in this category
|
||||||
|
pub channels: Option<Vec<String>>,
|
||||||
|
/// Fields to remove from category object
|
||||||
|
pub remove: Option<Vec<FieldsCategory>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum FieldsCategory {
|
||||||
|
DefaultPermissions
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DataDefaultCategoryPermissions {
|
||||||
|
pub permissions: Override
|
||||||
|
}
|
||||||
|
|
||||||
/// System message channel assignments
|
/// System message channel assignments
|
||||||
pub struct SystemMessageChannels {
|
pub struct SystemMessageChannels {
|
||||||
/// ID of channel to send user join messages in
|
/// ID of channel to send user join messages in
|
||||||
@@ -226,10 +263,6 @@ auto_derived!(
|
|||||||
pub icon: Option<String>,
|
pub icon: Option<String>,
|
||||||
/// Attachment Id for banner
|
/// Attachment Id for banner
|
||||||
pub banner: Option<String>,
|
pub banner: Option<String>,
|
||||||
|
|
||||||
/// Category structure for server
|
|
||||||
#[cfg_attr(feature = "validator", validate)]
|
|
||||||
pub categories: Option<Vec<Category>>,
|
|
||||||
/// System message configuration
|
/// System message configuration
|
||||||
pub system_messages: Option<SystemMessageChannels>,
|
pub system_messages: Option<SystemMessageChannels>,
|
||||||
|
|
||||||
|
|||||||
@@ -68,6 +68,26 @@ pub async fn calculate_server_permissions<P: PermissionQuery>(query: &mut P) ->
|
|||||||
permissions
|
permissions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn calculate_category_permissions<P: PermissionQuery>(query: &mut P) -> PermissionValue {
|
||||||
|
let mut permissions = calculate_server_permissions(query).await;
|
||||||
|
|
||||||
|
permissions.apply(query.get_default_category_permissions().await);
|
||||||
|
|
||||||
|
for role_override in query.get_our_category_role_overrides().await {
|
||||||
|
permissions.apply(role_override)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !permissions.has_channel_permission(ChannelPermission::ViewChannel) {
|
||||||
|
permissions.revoke_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
if query.are_we_timed_out().await {
|
||||||
|
permissions.restrict(*ALLOW_IN_TIMEOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
permissions
|
||||||
|
}
|
||||||
|
|
||||||
/// Calculate permissions against a channel
|
/// Calculate permissions against a channel
|
||||||
pub async fn calculate_channel_permissions<P: PermissionQuery>(query: &mut P) -> PermissionValue {
|
pub async fn calculate_channel_permissions<P: PermissionQuery>(query: &mut P) -> PermissionValue {
|
||||||
if query.are_we_privileged().await {
|
if query.are_we_privileged().await {
|
||||||
@@ -109,11 +129,19 @@ pub async fn calculate_channel_permissions<P: PermissionQuery>(query: &mut P) ->
|
|||||||
}
|
}
|
||||||
ChannelType::ServerChannel => {
|
ChannelType::ServerChannel => {
|
||||||
query.set_server_from_channel().await;
|
query.set_server_from_channel().await;
|
||||||
|
query.set_category_from_channel().await;
|
||||||
|
|
||||||
if query.are_we_server_owner().await {
|
if query.are_we_server_owner().await {
|
||||||
ChannelPermission::GrantAllSafe.into()
|
ChannelPermission::GrantAllSafe.into()
|
||||||
} else if query.are_we_a_member().await {
|
} else if query.are_we_a_member().await {
|
||||||
let mut permissions = calculate_server_permissions(query).await;
|
let mut permissions = calculate_server_permissions(query).await;
|
||||||
|
|
||||||
|
permissions.apply(query.get_default_category_permissions().await);
|
||||||
|
|
||||||
|
for role_override in query.get_our_category_role_overrides().await {
|
||||||
|
permissions.apply(role_override)
|
||||||
|
}
|
||||||
|
|
||||||
permissions.apply(query.get_default_channel_permissions().await);
|
permissions.apply(query.get_default_channel_permissions().await);
|
||||||
|
|
||||||
for role_override in query.get_our_channel_role_overrides().await {
|
for role_override in query.get_our_channel_role_overrides().await {
|
||||||
|
|||||||
@@ -96,8 +96,10 @@ pub enum ChannelPermission {
|
|||||||
/// Mention roles
|
/// Mention roles
|
||||||
MentionRoles = 1 << 38,
|
MentionRoles = 1 << 38,
|
||||||
|
|
||||||
|
MoveChannels = 1 << 39,
|
||||||
|
|
||||||
// * Misc. permissions
|
// * Misc. permissions
|
||||||
// % Bits 38 to 52: free area
|
// % Bits 40 to 52: free area
|
||||||
// % Bits 53 to 64: do not use
|
// % Bits 53 to 64: do not use
|
||||||
|
|
||||||
// * Grant all permissions
|
// * Grant all permissions
|
||||||
|
|||||||
@@ -91,6 +91,18 @@ async fn validate_user_permissions() {
|
|||||||
async fn set_server_from_channel(&mut self) {
|
async fn set_server_from_channel(&mut self) {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_default_category_permissions(&mut self) -> Override {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_our_category_role_overrides(&mut self) -> Vec<Override> {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn set_category_from_channel(&mut self) {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,6 +195,18 @@ async fn validate_group_permissions() {
|
|||||||
async fn set_server_from_channel(&mut self) {
|
async fn set_server_from_channel(&mut self) {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_default_category_permissions(&mut self) -> Override {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_our_category_role_overrides(&mut self) -> Vec<Override> {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn set_category_from_channel(&mut self) {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,6 +311,18 @@ async fn validate_server_permissions() {
|
|||||||
async fn set_server_from_channel(&mut self) {
|
async fn set_server_from_channel(&mut self) {
|
||||||
// no-op
|
// no-op
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_default_category_permissions(&mut self) -> Override {
|
||||||
|
Override { allow: 0, deny: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_our_category_role_overrides(&mut self) -> Vec<Override> {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn set_category_from_channel(&mut self) {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -373,5 +409,126 @@ async fn validate_timed_out_member() {
|
|||||||
async fn set_server_from_channel(&mut self) {
|
async fn set_server_from_channel(&mut self) {
|
||||||
// no-op
|
// no-op
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_default_category_permissions(&mut self) -> Override {
|
||||||
|
Override { allow: 0, deny: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_our_category_role_overrides(&mut self) -> Vec<Override> {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn set_category_from_channel(&mut self) {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_std::test]
|
||||||
|
async fn validate_category_permissions() {
|
||||||
|
/// Scenario in which we are in a channel in a server and has a role
|
||||||
|
/// Server defaults let us send messages, view channels
|
||||||
|
/// Category default denies sending, allows reacting
|
||||||
|
/// Category role allows sending message
|
||||||
|
struct Scenario {}
|
||||||
|
let mut query = Scenario {};
|
||||||
|
|
||||||
|
let perms = calculate_channel_permissions(&mut query).await;
|
||||||
|
let value: u64 = perms.into();
|
||||||
|
assert_eq!(value, ChannelPermission::ViewChannel as u64 | ChannelPermission::React as u64 | ChannelPermission::SendMessage as u64);
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl PermissionQuery for Scenario {
|
||||||
|
async fn are_we_privileged(&mut self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn are_we_a_bot(&mut self) -> bool {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn are_the_users_same(&mut self) -> bool {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn user_relationship(&mut self) -> RelationshipStatus {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn user_is_bot(&mut self) -> bool {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn have_mutual_connection(&mut self) -> bool {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn are_we_server_owner(&mut self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn are_we_a_member(&mut self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_default_server_permissions(&mut self) -> u64 {
|
||||||
|
ChannelPermission::SendMessage as u64 | ChannelPermission::ViewChannel as u64
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_our_server_role_overrides(&mut self) -> Vec<Override> {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn are_we_timed_out(&mut self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_channel_type(&mut self) -> ChannelType {
|
||||||
|
ChannelType::ServerChannel
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_default_channel_permissions(&mut self) -> Override {
|
||||||
|
Override { allow: 0, deny: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_our_channel_role_overrides(&mut self) -> Vec<Override> {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn do_we_own_the_channel(&mut self) -> bool {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn are_we_part_of_the_channel(&mut self) -> bool {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn set_recipient_as_user(&mut self) {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn set_server_from_channel(&mut self) {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_default_category_permissions(&mut self) -> Override {
|
||||||
|
Override {
|
||||||
|
allow: ChannelPermission::React as u64,
|
||||||
|
deny: ChannelPermission::SendMessage as u64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_our_category_role_overrides(&mut self) -> Vec<Override> {
|
||||||
|
vec![
|
||||||
|
Override {
|
||||||
|
allow: ChannelPermission::SendMessage as u64,
|
||||||
|
deny: 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn set_category_from_channel(&mut self) {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -51,6 +51,12 @@ pub trait PermissionQuery {
|
|||||||
/// Get the ordered role overrides (from lowest to highest) for this member in this channel
|
/// Get the ordered role overrides (from lowest to highest) for this member in this channel
|
||||||
async fn get_our_channel_role_overrides(&mut self) -> Vec<Override>;
|
async fn get_our_channel_role_overrides(&mut self) -> Vec<Override>;
|
||||||
|
|
||||||
|
/// Get the default category permissions
|
||||||
|
async fn get_default_category_permissions(&mut self) -> Override;
|
||||||
|
|
||||||
|
/// Get the ordered role overrides (from lowest to highest) for this member in this category
|
||||||
|
async fn get_our_category_role_overrides(&mut self) -> Vec<Override>;
|
||||||
|
|
||||||
/// Do we own this group or saved messages channel if it is one of those?
|
/// Do we own this group or saved messages channel if it is one of those?
|
||||||
async fn do_we_own_the_channel(&mut self) -> bool;
|
async fn do_we_own_the_channel(&mut self) -> bool;
|
||||||
|
|
||||||
@@ -64,4 +70,7 @@ pub trait PermissionQuery {
|
|||||||
/// Set the current server as the server owning this channel
|
/// Set the current server as the server owning this channel
|
||||||
/// (this will only ever be called for server channels, use unimplemented!() for other code paths)
|
/// (this will only ever be called for server channels, use unimplemented!() for other code paths)
|
||||||
async fn set_server_from_channel(&mut self);
|
async fn set_server_from_channel(&mut self);
|
||||||
|
|
||||||
|
//// SEts the current category as the category the channel is in
|
||||||
|
async fn set_category_from_channel(&mut self);
|
||||||
}
|
}
|
||||||
@@ -42,12 +42,14 @@ impl IntoResponse for Error {
|
|||||||
ErrorType::Banned => StatusCode::FORBIDDEN,
|
ErrorType::Banned => StatusCode::FORBIDDEN,
|
||||||
ErrorType::AlreadyInServer => StatusCode::CONFLICT,
|
ErrorType::AlreadyInServer => StatusCode::CONFLICT,
|
||||||
ErrorType::CannotTimeoutYourself => StatusCode::BAD_REQUEST,
|
ErrorType::CannotTimeoutYourself => StatusCode::BAD_REQUEST,
|
||||||
|
ErrorType::UnknownCategory => StatusCode::NOT_FOUND,
|
||||||
|
|
||||||
ErrorType::TooManyServers { .. } => StatusCode::BAD_REQUEST,
|
ErrorType::TooManyServers { .. } => StatusCode::BAD_REQUEST,
|
||||||
ErrorType::TooManyEmbeds { .. } => StatusCode::BAD_REQUEST,
|
ErrorType::TooManyEmbeds { .. } => StatusCode::BAD_REQUEST,
|
||||||
ErrorType::TooManyEmoji { .. } => StatusCode::BAD_REQUEST,
|
ErrorType::TooManyEmoji { .. } => StatusCode::BAD_REQUEST,
|
||||||
ErrorType::TooManyChannels { .. } => StatusCode::BAD_REQUEST,
|
ErrorType::TooManyChannels { .. } => StatusCode::BAD_REQUEST,
|
||||||
ErrorType::TooManyRoles { .. } => StatusCode::BAD_REQUEST,
|
ErrorType::TooManyRoles { .. } => StatusCode::BAD_REQUEST,
|
||||||
|
ErrorType::TooManyCategories { .. } => StatusCode::BAD_REQUEST,
|
||||||
|
|
||||||
ErrorType::ReachedMaximumBots => StatusCode::BAD_REQUEST,
|
ErrorType::ReachedMaximumBots => StatusCode::BAD_REQUEST,
|
||||||
ErrorType::IsBot => StatusCode::BAD_REQUEST,
|
ErrorType::IsBot => StatusCode::BAD_REQUEST,
|
||||||
|
|||||||
@@ -115,6 +115,10 @@ pub enum ErrorType {
|
|||||||
TooManyRoles {
|
TooManyRoles {
|
||||||
max: usize,
|
max: usize,
|
||||||
},
|
},
|
||||||
|
TooManyCategories {
|
||||||
|
max: usize,
|
||||||
|
},
|
||||||
|
UnknownCategory,
|
||||||
AlreadyInServer,
|
AlreadyInServer,
|
||||||
CannotTimeoutYourself,
|
CannotTimeoutYourself,
|
||||||
|
|
||||||
|
|||||||
@@ -49,12 +49,14 @@ impl<'r> Responder<'r, 'static> for Error {
|
|||||||
ErrorType::Banned => Status::Forbidden,
|
ErrorType::Banned => Status::Forbidden,
|
||||||
ErrorType::AlreadyInServer => Status::Conflict,
|
ErrorType::AlreadyInServer => Status::Conflict,
|
||||||
ErrorType::CannotTimeoutYourself => Status::BadRequest,
|
ErrorType::CannotTimeoutYourself => Status::BadRequest,
|
||||||
|
ErrorType::UnknownCategory => Status::NotFound,
|
||||||
|
|
||||||
ErrorType::TooManyServers { .. } => Status::BadRequest,
|
ErrorType::TooManyServers { .. } => Status::BadRequest,
|
||||||
ErrorType::TooManyEmbeds { .. } => Status::BadRequest,
|
ErrorType::TooManyEmbeds { .. } => Status::BadRequest,
|
||||||
ErrorType::TooManyEmoji { .. } => Status::BadRequest,
|
ErrorType::TooManyEmoji { .. } => Status::BadRequest,
|
||||||
ErrorType::TooManyChannels { .. } => Status::BadRequest,
|
ErrorType::TooManyChannels { .. } => Status::BadRequest,
|
||||||
ErrorType::TooManyRoles { .. } => Status::BadRequest,
|
ErrorType::TooManyRoles { .. } => Status::BadRequest,
|
||||||
|
ErrorType::TooManyCategories { .. } => Status::BadRequest,
|
||||||
|
|
||||||
ErrorType::ReachedMaximumBots => Status::BadRequest,
|
ErrorType::ReachedMaximumBots => Status::BadRequest,
|
||||||
ErrorType::IsBot => Status::BadRequest,
|
ErrorType::IsBot => Status::BadRequest,
|
||||||
|
|||||||
@@ -192,6 +192,7 @@ mod test {
|
|||||||
d: ChannelPermission::ViewChannel as i64,
|
d: ChannelPermission::ViewChannel as i64,
|
||||||
}),
|
}),
|
||||||
last_message_id: None,
|
last_message_id: None,
|
||||||
|
parent: None,
|
||||||
};
|
};
|
||||||
locked_channel
|
locked_channel
|
||||||
.update(&harness.db, partial, vec![])
|
.update(&harness.db, partial, vec![])
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
use revolt_config::config;
|
||||||
|
use revolt_database::{
|
||||||
|
util::{permissions::DatabasePermissionQuery, reference::Reference}, Category, Database, Role, User
|
||||||
|
};
|
||||||
|
use revolt_models::v0;
|
||||||
|
use revolt_permissions::{calculate_server_permissions, ChannelPermission};
|
||||||
|
use revolt_result::{create_error, Result};
|
||||||
|
use rocket::{serde::json::Json, State};
|
||||||
|
use validator::Validate;
|
||||||
|
|
||||||
|
/// # Create a category
|
||||||
|
///
|
||||||
|
/// Creates a new server category.
|
||||||
|
#[openapi(tag = "Server Categories")]
|
||||||
|
#[post("/<target>/categories", data = "<data>")]
|
||||||
|
pub async fn create(
|
||||||
|
db: &State<Database>,
|
||||||
|
user: User,
|
||||||
|
target: Reference,
|
||||||
|
data: Json<v0::DataCreateCategory>,
|
||||||
|
) -> Result<Json<v0::Category>> {
|
||||||
|
let data = data.into_inner();
|
||||||
|
data.validate().map_err(|error| {
|
||||||
|
create_error!(FailedValidation {
|
||||||
|
error: error.to_string()
|
||||||
|
})
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let mut server = target.as_server(db).await?;
|
||||||
|
let mut query = DatabasePermissionQuery::new(db, &user).server(&server);
|
||||||
|
calculate_server_permissions(&mut query)
|
||||||
|
.await
|
||||||
|
.throw_if_lacking_channel_permission(ChannelPermission::ManageChannel)?;
|
||||||
|
|
||||||
|
let config = config().await;
|
||||||
|
if server.categories.len() >= config.features.limits.global.server_categories {
|
||||||
|
return Err(create_error!(TooManyCategories {
|
||||||
|
max: config.features.limits.global.server_categories,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
let category = Category::create(db, &mut server, data).await?;
|
||||||
|
|
||||||
|
Ok(Json(category.into()))
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
use revolt_config::config;
|
||||||
|
use revolt_database::{
|
||||||
|
util::{permissions::DatabasePermissionQuery, reference::Reference}, Category, Database, PartialCategory, Role, User
|
||||||
|
};
|
||||||
|
use revolt_models::v0::{self, DataEditCategory};
|
||||||
|
use revolt_permissions::{calculate_server_permissions, ChannelPermission};
|
||||||
|
use revolt_result::{create_error, Result};
|
||||||
|
use rocket::{serde::json::Json, State};
|
||||||
|
use rocket_empty::EmptyResponse;
|
||||||
|
use validator::Validate;
|
||||||
|
|
||||||
|
/// # Edits a category
|
||||||
|
///
|
||||||
|
/// Edits a server category.
|
||||||
|
#[openapi(tag = "Server Categories")]
|
||||||
|
#[delete("/<server>/categories/<category>")]
|
||||||
|
pub async fn delete(
|
||||||
|
db: &State<Database>,
|
||||||
|
user: User,
|
||||||
|
server: Reference,
|
||||||
|
category: String
|
||||||
|
) -> Result<EmptyResponse> {
|
||||||
|
let mut server = server.as_server(db).await?;
|
||||||
|
|
||||||
|
let category = server.categories
|
||||||
|
.get(&category)
|
||||||
|
.ok_or(create_error!(UnknownCategory))?
|
||||||
|
.clone();
|
||||||
|
|
||||||
|
let mut query = DatabasePermissionQuery::new(db, &user).server(&server);
|
||||||
|
calculate_server_permissions(&mut query)
|
||||||
|
.await
|
||||||
|
.throw_if_lacking_channel_permission(ChannelPermission::ManageChannel)?;
|
||||||
|
|
||||||
|
category.delete(db, &mut server).await?;
|
||||||
|
|
||||||
|
Ok(EmptyResponse)
|
||||||
|
}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
use revolt_config::config;
|
||||||
|
use revolt_database::{
|
||||||
|
util::{permissions::DatabasePermissionQuery, reference::Reference}, Category, Channel, Database, PartialCategory, PartialChannel, Role, User
|
||||||
|
};
|
||||||
|
use revolt_models::v0::{self, DataEditCategory};
|
||||||
|
use revolt_permissions::{calculate_server_permissions, ChannelPermission};
|
||||||
|
use revolt_result::{create_error, Result};
|
||||||
|
use rocket::{serde::json::Json, State};
|
||||||
|
use validator::Validate;
|
||||||
|
|
||||||
|
/// # Edits a category
|
||||||
|
///
|
||||||
|
/// Edits a server category.
|
||||||
|
#[openapi(tag = "Server Categories")]
|
||||||
|
#[patch("/<server>/categories/<category>", data = "<data>")]
|
||||||
|
pub async fn edit(
|
||||||
|
db: &State<Database>,
|
||||||
|
user: User,
|
||||||
|
server: Reference,
|
||||||
|
category: String,
|
||||||
|
data: Json<v0::DataEditCategory>,
|
||||||
|
) -> Result<Json<v0::Category>> {
|
||||||
|
let data = data.into_inner();
|
||||||
|
data.validate().map_err(|error| {
|
||||||
|
create_error!(FailedValidation {
|
||||||
|
error: error.to_string()
|
||||||
|
})
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let mut server = server.as_server(db).await?;
|
||||||
|
|
||||||
|
let mut category = server.categories
|
||||||
|
.get(&category)
|
||||||
|
.ok_or(create_error!(UnknownCategory))?
|
||||||
|
.clone();
|
||||||
|
|
||||||
|
let mut query = DatabasePermissionQuery::new(db, &user).server(&server).category(&category);
|
||||||
|
let permissions = calculate_server_permissions(&mut query)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
permissions.throw_if_lacking_channel_permission(ChannelPermission::ManageChannel)?;
|
||||||
|
|
||||||
|
let DataEditCategory {
|
||||||
|
title,
|
||||||
|
mut channels,
|
||||||
|
remove
|
||||||
|
} = data;
|
||||||
|
|
||||||
|
if channels.is_some() {
|
||||||
|
permissions.throw_if_lacking_channel_permission(ChannelPermission::MoveChannels)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if any channel we are adding to this category are in another category we need to make sure we have MoveChannels in the category before moving it over.
|
||||||
|
for category in server.categories.values() {
|
||||||
|
if category.channels.iter().any(|c| channels.as_ref().is_some_and(|cs| cs.contains(c))) {
|
||||||
|
let mut category_query = query.clone().category(category);
|
||||||
|
|
||||||
|
calculate_server_permissions(&mut category_query)
|
||||||
|
.await
|
||||||
|
.throw_if_lacking_channel_permission(ChannelPermission::MoveChannels)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove the channels from any existing categories to avoid it having two
|
||||||
|
for category in server.categories.values_mut() {
|
||||||
|
category.channels.retain(|c| channels.as_ref().is_some_and(|cs| cs.contains(c)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// only keep channels which exist in the server
|
||||||
|
if let Some(channels) = &mut channels {
|
||||||
|
channels.retain(|c| server.channels.contains(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
// unset parent from all channels which are removed from the category
|
||||||
|
for channel_id in &category.channels {
|
||||||
|
if channels.as_ref().is_some_and(|cs| !cs.contains(channel_id)) {
|
||||||
|
db.update_channel(channel_id, &PartialChannel { parent: None, ..Default::default() }, Vec::new()).await?;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// update the category with the new values
|
||||||
|
category.update(
|
||||||
|
db,
|
||||||
|
&mut server,
|
||||||
|
PartialCategory {
|
||||||
|
title,
|
||||||
|
channels: channels.clone(),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
remove
|
||||||
|
.map(|v| v.into_iter().map(Into::into).collect())
|
||||||
|
.unwrap_or_default()
|
||||||
|
).await?;
|
||||||
|
|
||||||
|
let channels = db.fetch_channels(&channels.unwrap_or_default()).await?;
|
||||||
|
|
||||||
|
// update all channels to have the parent set
|
||||||
|
for channel in channels {
|
||||||
|
if let Channel::TextChannel { ref parent, .. } | Channel::VoiceChannel { ref parent, .. } = channel {
|
||||||
|
if parent.as_ref() != Some(&category.id) {
|
||||||
|
db.update_channel(channel.id(), &PartialChannel { parent: Some(category.id.clone()), ..Default::default() }, Vec::new()).await?;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Json(category.into()))
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
use revolt_database::{
|
||||||
|
util::{permissions::DatabasePermissionQuery, reference::Reference}, Database, User
|
||||||
|
};
|
||||||
|
use revolt_models::v0;
|
||||||
|
use revolt_permissions::{calculate_category_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 this channel.
|
||||||
|
///
|
||||||
|
/// Channel must be a `TextChannel` or `VoiceChannel`.
|
||||||
|
#[openapi(tag = "Channel Permissions")]
|
||||||
|
#[put("/<server>/categories/<category>/permissions/<role_id>", data = "<data>", rank = 2)]
|
||||||
|
pub async fn set_role_permissions(
|
||||||
|
db: &State<Database>,
|
||||||
|
user: User,
|
||||||
|
server: Reference,
|
||||||
|
category: String,
|
||||||
|
role_id: String,
|
||||||
|
data: Json<v0::DataSetRolePermissions>,
|
||||||
|
) -> Result<Json<v0::Category>> {
|
||||||
|
let mut server = server.as_server(db).await?;
|
||||||
|
let mut category = server.categories.get(&category).ok_or(create_error!(UnknownCategory))?.clone();
|
||||||
|
|
||||||
|
let mut query = DatabasePermissionQuery::new(db, &user).server(&server).category(&category);
|
||||||
|
let permissions = calculate_category_permissions(&mut query).await;
|
||||||
|
|
||||||
|
permissions.throw_if_lacking_channel_permission(ChannelPermission::ManagePermissions)?;
|
||||||
|
|
||||||
|
if let Some(role) = server.roles.get(&role_id) {
|
||||||
|
if role.rank <= query.get_member_rank().unwrap_or(i64::MIN) {
|
||||||
|
return Err(create_error!(NotElevated));
|
||||||
|
}
|
||||||
|
|
||||||
|
let current_value: Override = role.permissions.into();
|
||||||
|
permissions
|
||||||
|
.throw_permission_override(current_value, &data.permissions)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
category
|
||||||
|
.set_role_permission(db, &mut server, role_id, data.permissions.clone().into())
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(Json(category.into()))
|
||||||
|
} else {
|
||||||
|
Err(create_error!(NotFound))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
use revolt_database::{
|
||||||
|
util::{permissions::DatabasePermissionQuery, reference::Reference}, Channel, Database, PartialCategory, PartialChannel, User
|
||||||
|
};
|
||||||
|
use revolt_models::v0::{self, DataDefaultChannelPermissions};
|
||||||
|
use revolt_permissions::{calculate_category_permissions, calculate_channel_permissions, ChannelPermission};
|
||||||
|
use revolt_result::{create_error, Result};
|
||||||
|
use rocket::{serde::json::Json, State};
|
||||||
|
|
||||||
|
/// # Set Default Permission
|
||||||
|
///
|
||||||
|
/// Sets permissions for the default role in this channel.
|
||||||
|
///
|
||||||
|
/// Channel must be a `Group`, `TextChannel` or `VoiceChannel`.
|
||||||
|
#[openapi(tag = "Category Permissions")]
|
||||||
|
#[put("/<server>/categories/<target>/permissions/default", data = "<data>", rank = 1)]
|
||||||
|
pub async fn set_default_permissions(
|
||||||
|
db: &State<Database>,
|
||||||
|
user: User,
|
||||||
|
server: Reference,
|
||||||
|
target: String,
|
||||||
|
data: Json<v0::DataDefaultCategoryPermissions>,
|
||||||
|
) -> Result<Json<v0::Category>> {
|
||||||
|
let data = data.into_inner();
|
||||||
|
|
||||||
|
let mut server = server.as_server(db).await?;
|
||||||
|
let mut category = server.categories.get(&target).ok_or(create_error!(UnknownCategory))?.clone();
|
||||||
|
|
||||||
|
let mut query = DatabasePermissionQuery::new(db, &user).server(&server).category(&category);
|
||||||
|
let permissions = calculate_category_permissions(&mut query).await;
|
||||||
|
|
||||||
|
permissions.throw_if_lacking_channel_permission(ChannelPermission::ManagePermissions)?;
|
||||||
|
|
||||||
|
permissions
|
||||||
|
.throw_permission_override(category.default_permissions.map(|x| x.into()), &data.permissions)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
category
|
||||||
|
.update(
|
||||||
|
db,
|
||||||
|
&mut server,
|
||||||
|
PartialCategory {
|
||||||
|
default_permissions: Some(data.permissions.into()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
vec![],
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(Json(category.into()))
|
||||||
|
}
|
||||||
@@ -23,6 +23,11 @@ mod server_create;
|
|||||||
mod server_delete;
|
mod server_delete;
|
||||||
mod server_edit;
|
mod server_edit;
|
||||||
mod server_fetch;
|
mod server_fetch;
|
||||||
|
mod category_create;
|
||||||
|
mod category_edit;
|
||||||
|
mod category_delete;
|
||||||
|
mod category_permissions_set_default;
|
||||||
|
mod category_permissions_set;
|
||||||
|
|
||||||
pub fn routes() -> (Vec<Route>, OpenApi) {
|
pub fn routes() -> (Vec<Route>, OpenApi) {
|
||||||
openapi_get_routes_spec![
|
openapi_get_routes_spec![
|
||||||
@@ -47,6 +52,11 @@ pub fn routes() -> (Vec<Route>, OpenApi) {
|
|||||||
roles_delete::delete,
|
roles_delete::delete,
|
||||||
permissions_set::set_role_permission,
|
permissions_set::set_role_permission,
|
||||||
permissions_set_default::set_default_permissions,
|
permissions_set_default::set_default_permissions,
|
||||||
emoji_list::list_emoji
|
emoji_list::list_emoji,
|
||||||
|
category_create::create,
|
||||||
|
category_edit::edit,
|
||||||
|
category_delete::delete,
|
||||||
|
category_permissions_set_default::set_default_permissions,
|
||||||
|
category_permissions_set::set_role_permissions,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
use std::collections::HashSet;
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
use revolt_database::{
|
use revolt_database::{
|
||||||
util::{permissions::DatabasePermissionQuery, reference::Reference},
|
util::{permissions::DatabasePermissionQuery, reference::Reference},
|
||||||
@@ -38,7 +38,6 @@ pub async fn edit(
|
|||||||
&& data.icon.is_none()
|
&& data.icon.is_none()
|
||||||
&& data.banner.is_none()
|
&& data.banner.is_none()
|
||||||
&& data.system_messages.is_none()
|
&& data.system_messages.is_none()
|
||||||
&& data.categories.is_none()
|
|
||||||
// && data.nsfw.is_none()
|
// && data.nsfw.is_none()
|
||||||
&& data.flags.is_none()
|
&& data.flags.is_none()
|
||||||
&& data.analytics.is_none()
|
&& data.analytics.is_none()
|
||||||
@@ -63,18 +62,11 @@ pub async fn edit(
|
|||||||
{
|
{
|
||||||
return Err(create_error!(NotPrivileged));
|
return Err(create_error!(NotPrivileged));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Changing categories requires manage channel
|
|
||||||
if data.categories.is_some() {
|
|
||||||
permissions.throw_if_lacking_channel_permission(ChannelPermission::ManageChannel)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let v0::DataEditServer {
|
let v0::DataEditServer {
|
||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
icon,
|
icon,
|
||||||
banner,
|
banner,
|
||||||
categories,
|
|
||||||
system_messages,
|
system_messages,
|
||||||
flags,
|
flags,
|
||||||
// nsfw,
|
// nsfw,
|
||||||
@@ -86,7 +78,6 @@ pub async fn edit(
|
|||||||
let mut partial = PartialServer {
|
let mut partial = PartialServer {
|
||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
categories: categories.map(|v| v.into_iter().map(Into::into).collect()),
|
|
||||||
system_messages: system_messages.map(Into::into),
|
system_messages: system_messages.map(Into::into),
|
||||||
flags,
|
flags,
|
||||||
// nsfw,
|
// nsfw,
|
||||||
@@ -119,23 +110,6 @@ pub async fn edit(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(categories) = &mut partial.categories {
|
|
||||||
let mut channel_ids = HashSet::new();
|
|
||||||
for category in categories {
|
|
||||||
for channel in &category.channels {
|
|
||||||
if channel_ids.contains(channel) {
|
|
||||||
return Err(create_error!(InvalidOperation));
|
|
||||||
}
|
|
||||||
|
|
||||||
channel_ids.insert(channel.to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
category
|
|
||||||
.channels
|
|
||||||
.retain(|item| server.channels.contains(item));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Apply new icon
|
// 3. Apply new icon
|
||||||
if let Some(icon) = icon {
|
if let Some(icon) = icon {
|
||||||
partial.icon = Some(File::use_server_icon(db, &icon, &server.id, &user.id).await?);
|
partial.icon = Some(File::use_server_icon(db, &icon, &server.id, &user.id).await?);
|
||||||
|
|||||||
Reference in new issue
Block a user