From 020f2a1b1ae8cd1f85863ec557d8dd0b3d02a2c6 Mon Sep 17 00:00:00 2001 From: Paul Makles Date: Mon, 6 Jun 2022 11:37:37 +0100 Subject: [PATCH] fix: enforce role permissions on member_edit --- Cargo.lock | 2 +- crates/delta/Cargo.toml | 2 +- .../delta/src/routes/servers/member_edit.rs | 93 +++++++++++++++++-- 3 files changed, 88 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9630eb56..81ccab57 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2992,7 +2992,7 @@ dependencies = [ [[package]] name = "revolt-delta" -version = "0.5.3-5-patch.2" +version = "0.5.3-6" dependencies = [ "async-channel", "async-std", diff --git a/crates/delta/Cargo.toml b/crates/delta/Cargo.toml index 2c3668ee..1d381024 100644 --- a/crates/delta/Cargo.toml +++ b/crates/delta/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "revolt-delta" -version = "0.5.3-5-patch.2" +version = "0.5.3-6" license = "AGPL-3.0-or-later" authors = ["Paul Makles "] edition = "2018" diff --git a/crates/delta/src/routes/servers/member_edit.rs b/crates/delta/src/routes/servers/member_edit.rs index 93a75603..f0797dd0 100644 --- a/crates/delta/src/routes/servers/member_edit.rs +++ b/crates/delta/src/routes/servers/member_edit.rs @@ -1,9 +1,11 @@ +use std::collections::HashSet; + use revolt_quark::{ models::{ server_member::{FieldsMember, PartialMember}, File, Member, User, }, - perms, Db, Error, Ref, Result, + perms, Db, Error, Permission, Ref, Result, }; use rocket::serde::json::Json; @@ -41,11 +43,91 @@ pub async fn req( data.validate() .map_err(|error| Error::FailedValidation { error })?; - let server = server.as_server(db).await?; - perms(&user).server(&server).calc(db).await?; - + // 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); + // Check permissions in server + let mut required = vec![]; + + if data.nickname.is_some() + || data + .remove + .as_ref() + .map(|x| x.contains(&FieldsMember::Nickname)) + .unwrap_or_default() + { + if user.id == member.id.user { + required.push(Permission::ChangeNickname); + } else { + required.push(Permission::ManageNicknames); + } + } + + if data.avatar.is_some() + || data + .remove + .as_ref() + .map(|x| x.contains(&FieldsMember::Avatar)) + .unwrap_or_default() + { + if user.id == member.id.user { + required.push(Permission::ChangeAvatar); + } else { + return Err(Error::InvalidOperation); + } + } + + if data.roles.is_some() + || data + .remove + .as_ref() + .map(|x| x.contains(&FieldsMember::Roles)) + .unwrap_or_default() + { + required.push(Permission::AssignRoles); + } + + for permission in required { + permissions.throw_permission(db, permission).await?; + } + + // Resolve our ranking + let our_ranking = permissions.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 + { + return Err(Error::NotElevated); + } + + // Check permissions against roles in diff + if let Some(roles) = &data.roles { + let fallback = vec![]; + let current_roles = member + .roles + .as_ref() + .unwrap_or(&fallback) + .iter() + .collect::>(); + + let new_roles = roles.iter().collect::>(); + let added_roles: Vec<&&String> = new_roles.difference(¤t_roles).collect(); + + for role_id in added_roles { + if let Some(role) = server.roles.remove(*role_id) { + if role.rank <= our_ranking { + return Err(Error::NotElevated); + } + } else { + return Err(Error::InvalidRole); + } + } + } + + // Apply edits to the member object let DataMemberEdit { nickname, avatar, @@ -59,9 +141,6 @@ pub async fn req( ..Default::default() }; - // ! FIXME: calculate permission against member - // ! FIXME: also check roles exist lol - // 1. Remove fields from object if let Some(fields) = &remove { if fields.contains(&FieldsMember::Avatar) {