diff --git a/src/routes/servers/member_edit.rs b/src/routes/servers/member_edit.rs index 2b34dd589402b3adc74891f06599f419a297541a..79b342a0e93fda3876c0e69a30f2d6294eedc3f3 100644 --- a/src/routes/servers/member_edit.rs +++ b/src/routes/servers/member_edit.rs @@ -1,3 +1,5 @@ +use std::collections::HashSet; + use crate::notifications::events::ClientboundNotification; use crate::util::result::{Error, Result}; use crate::{database::*, notifications::events::RemoveMemberField}; @@ -12,6 +14,7 @@ pub struct Data { #[validate(length(min = 1, max = 32))] nickname: Option<String>, avatar: Option<String>, + roles: Option<Vec<String>>, remove: Option<RemoveMemberField>, } @@ -21,7 +24,7 @@ pub async fn req(user: User, server: Ref, target: String, data: Json<Data>) -> R data.validate() .map_err(|error| Error::FailedValidation { error })?; - if data.nickname.is_none() && data.avatar.is_none() && data.remove.is_none() { + if data.nickname.is_none() && data.avatar.is_none() && data.roles.is_none() && data.remove.is_none() { return Ok(()); } @@ -33,6 +36,10 @@ pub async fn req(user: User, server: Ref, target: String, data: Json<Data>) -> R .for_server() .await?; + if data.roles.is_some() && !perm.get_manage_roles() { + return Err(Error::MissingPermission); + } + if target.id.user == user.id { if (data.nickname.is_some() && !perm.get_change_nickname()) || (data.avatar.is_some() && !perm.get_change_avatar()) @@ -97,6 +104,18 @@ pub async fn req(user: User, server: Ref, target: String, data: Json<Data>) -> R remove_avatar = true; } + if let Some(role_ids) = &data.roles { + let mut ids = HashSet::new(); + + for role in role_ids { + if server.roles.contains_key(role) { + ids.insert(role.clone()); + } + } + + set.insert("roles", ids.into_iter().collect::<Vec<String>>()); + } + let mut operations = doc! {}; if set.len() > 0 { operations.insert("$set", &set); diff --git a/src/routes/servers/mod.rs b/src/routes/servers/mod.rs index 2f03c3578825a0eb9ae90d190f69b34ccf744172..15968de8f3f6d32df4ae9db6d854199f4a151924 100644 --- a/src/routes/servers/mod.rs +++ b/src/routes/servers/mod.rs @@ -20,6 +20,8 @@ mod invites_fetch; mod roles_create; mod roles_delete; +mod permissions_set; +mod permissions_set_default; pub fn routes() -> Vec<Route> { routes![ @@ -37,6 +39,8 @@ pub fn routes() -> Vec<Route> { ban_list::req, invites_fetch::req, roles_create::req, - roles_delete::req + roles_delete::req, + permissions_set::req, + permissions_set_default::req ] } diff --git a/src/routes/servers/permissions_set.rs b/src/routes/servers/permissions_set.rs new file mode 100644 index 0000000000000000000000000000000000000000..3e23a06c7908753a4ce7b0c38d03106ba68ffd2a --- /dev/null +++ b/src/routes/servers/permissions_set.rs @@ -0,0 +1,69 @@ +use mongodb::bson::doc; +use rocket_contrib::json::Json; +use serde::{Serialize, Deserialize}; + +use crate::database::*; +use crate::database::permissions::channel::ChannelPermission; +use crate::database::permissions::server::ServerPermission; +use crate::notifications::events::ClientboundNotification; +use crate::util::result::{Error, Result}; + +#[derive(Serialize, Deserialize)] +pub struct Data { + server: u32, + channel: u32 +} + +#[put("/<target>/permissions/<role_id>", data = "<data>")] +pub async fn req(user: User, target: Ref, role_id: String, data: Json<Data>) -> Result<()> { + let target = target.fetch_server().await?; + + let perm = permissions::PermissionCalculator::new(&user) + .with_server(&target) + .for_server() + .await?; + + if !perm.get_manage_roles() { + return Err(Error::MissingPermission); + } + + if !target.roles.contains_key(&role_id) { + return Err(Error::NotFound); + } + + let server_permissions: u32 = ServerPermission::View as u32 | data.server; + let channel_permissions: u32 = ChannelPermission::View as u32 | data.channel; + + get_collection("servers") + .update_one( + doc! { "_id": &target.id }, + doc! { + "$set": { + "roles.".to_owned() + &role_id + &".permissions": [ + server_permissions as i32, + channel_permissions as i32 + ] + } + }, + None + ) + .await + .map_err(|_| Error::DatabaseError { + operation: "update_one", + with: "server" + })?; + + ClientboundNotification::ServerRoleUpdate { + id: target.id.clone(), + role_id, + data: json!({ + "permissions": [ + server_permissions as i32, + channel_permissions as i32 + ] + }) + } + .publish(target.id); + + Ok(()) +} diff --git a/src/routes/servers/permissions_set_default.rs b/src/routes/servers/permissions_set_default.rs new file mode 100644 index 0000000000000000000000000000000000000000..d379092c7acd66aeb1f49d290cf0a1e55b1549cc --- /dev/null +++ b/src/routes/servers/permissions_set_default.rs @@ -0,0 +1,65 @@ +use mongodb::bson::doc; +use rocket_contrib::json::Json; +use serde::{Serialize, Deserialize}; + +use crate::database::*; +use crate::database::permissions::channel::ChannelPermission; +use crate::database::permissions::server::ServerPermission; +use crate::notifications::events::ClientboundNotification; +use crate::util::result::{Error, Result}; + +#[derive(Serialize, Deserialize)] +pub struct Data { + server: u32, + channel: u32 +} + +#[put("/<target>/permissions/default", data = "<data>", rank = 1)] +pub async fn req(user: User, target: Ref, data: Json<Data>) -> Result<()> { + let target = target.fetch_server().await?; + + let perm = permissions::PermissionCalculator::new(&user) + .with_server(&target) + .for_server() + .await?; + + if !perm.get_manage_roles() { + return Err(Error::MissingPermission); + } + + let server_permissions: u32 = ServerPermission::View as u32 | data.server; + let channel_permissions: u32 = ChannelPermission::View as u32 | data.channel; + + get_collection("servers") + .update_one( + doc! { "_id": &target.id }, + doc! { + "$set": { + "default_permissions": [ + server_permissions as i32, + channel_permissions as i32 + ] + } + }, + None + ) + .await + .map_err(|_| Error::DatabaseError { + operation: "update_one", + with: "server" + })?; + + ClientboundNotification::ServerUpdate { + id: target.id.clone(), + data: json!({ + "default_permissions": [ + server_permissions as i32, + channel_permissions as i32 + ] + }), + clear: None + } + .publish(target.id); + + Ok(()) +} diff --git a/src/routes/servers/roles_create.rs b/src/routes/servers/roles_create.rs index 6453642a5a0d0a905ca826e7c6f9b28953ae5c45..e55887c2e8c2b700845e2c8fa988d328ed9198f8 100644 --- a/src/routes/servers/roles_create.rs +++ b/src/routes/servers/roles_create.rs @@ -40,7 +40,7 @@ pub async fn req(user: User, target: Ref, data: Json<Data>) -> Result<JsonValue> get_collection("servers") .update_one( doc! { - "_id": &id + "_id": &target.id }, doc! { "$set": { diff --git a/src/routes/servers/roles_delete.rs b/src/routes/servers/roles_delete.rs index baab78aaaf4f864f9a34ff7d50053e5d1ca07b4f..09c7b2c513991d0c6d0ec27525f81a9a7ee1809a 100644 --- a/src/routes/servers/roles_delete.rs +++ b/src/routes/servers/roles_delete.rs @@ -20,7 +20,7 @@ pub async fn req(user: User, target: Ref, role_id: String) -> Result<()> { get_collection("servers") .update_one( doc! { - "_id": &role_id + "_id": &target.id }, doc! { "$unset": { @@ -34,9 +34,42 @@ pub async fn req(user: User, target: Ref, role_id: String) -> Result<()> { operation: "update_one", with: "servers" })?; - - // remove role from members - // remove role from channels + + get_collection("channels") + .update_one( + doc! { + "server": &target.id + }, + doc! { + "$unset": { + "role_permissions.".to_owned() + &role_id: 1 + } + }, + None + ) + .await + .map_err(|_| Error::DatabaseError { + operation: "update_one", + with: "channels" + })?; + + get_collection("server_members") + .update_many( + doc! { + "_id.server": &target.id + }, + doc! { + "$pull": { + "roles": &role_id + } + }, + None + ) + .await + .map_err(|_| Error::DatabaseError { + operation: "update_many", + with: "server_members" + })?; ClientboundNotification::ServerRoleDelete { id: target.id.clone(),