fix(permissions): reposition channel default permissions below role overrides (#847)

Signed-off-by: siq <siq@siqnole.dev>
Co-authored-by: siq <siq@siqnole.dev>
This commit is contained in:
William
2026-08-09 13:43:53 -04:00
committed by GitHub
parent e956de923e
commit 9ab2ae92a6
3 changed files with 148 additions and 4 deletions

View File

@@ -280,14 +280,30 @@ async fn calculate_members_permissions<'a>(
continue;
}
// Get the user's server permissions
let mut permission = calculate_server_permissions(&query.server, user, member);
// Get the server default permission, apply channel default override
let mut permission = PermissionValue::from(query.server.default_permissions);
if let Some(defaults) = channel_default_permissions {
permission.apply(defaults.into());
}
// Get the applicable role overrides
// Apply server role overrides
let mut server_roles = query.server
.roles
.iter()
.filter(|(id, _)| member.roles.contains(id))
.map(|(_, role)| {
let v: Override = role.permissions.into();
(role.rank, v)
})
.collect::<Vec<(i64, Override)>>();
server_roles.sort_by(|a, b| b.0.cmp(&a.0));
for (_, role_override) in server_roles {
permission.apply(role_override);
}
// Get the applicable channel role overrides
let mut roles = channel_role_permissions
.iter()
.filter(|(id, _)| member.roles.contains(id))
@@ -306,6 +322,10 @@ async fn calculate_members_permissions<'a>(
permission.apply(role_override)
}
if member.in_timeout() {
permission.restrict(*ALLOW_IN_TIMEOUT);
}
resp.insert(user.id.clone(), permission);
}

View File

@@ -122,13 +122,26 @@ pub async fn calculate_channel_permissions<P: PermissionQuery>(query: &mut P) ->
if query.are_we_server_owner().await {
ChannelPermission::GrantAllSafe.into()
} else if query.are_we_a_member().await {
let mut permissions = calculate_server_permissions(query).await;
let mut permissions = PermissionValue::from(query.get_default_server_permissions().await);
permissions.apply(query.get_default_channel_permissions().await);
for role_override in query.get_our_server_role_overrides().await {
permissions.apply(role_override);
}
for role_override in query.get_our_channel_role_overrides().await {
permissions.apply(role_override);
}
if !query.do_we_have_publish_overwrites().await {
permissions.revoke(ChannelPermission::Speak as u64);
permissions.revoke(ChannelPermission::Video as u64);
}
if !query.do_we_have_receive_overwrites().await {
permissions.revoke(ChannelPermission::Listen as u64);
}
if query.are_we_timed_out().await {
permissions.restrict(*ALLOW_IN_TIMEOUT);
}

View File

@@ -407,3 +407,114 @@ async fn validate_timed_out_member() {
}
}
}
#[tokio::test]
async fn validate_channel_default_below_server_roles() {
/// Scenario where:
/// - Server default allows ViewChannel.
/// - Channel default override denies SendMessage.
/// - Server role override allows SendMessage.
/// - We don't have channel role overrides.
/// Since default permissions should be below role permissions,
/// our server role override (allowing SendMessage) should take precedence
/// over the channel default permission (denying SendMessage).
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::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::ViewChannel as u64
}
async fn get_our_server_role_overrides(&mut self) -> Vec<Override> {
vec![Override {
allow: ChannelPermission::SendMessage as u64,
deny: 0,
}]
}
async fn are_we_timed_out(&mut self) -> bool {
false
}
async fn do_we_have_publish_overwrites(&mut self) -> bool {
true
}
async fn do_we_have_receive_overwrites(&mut self) -> bool {
true
}
async fn get_channel_type(&mut self) -> ChannelType {
ChannelType::ServerChannel
}
async fn get_default_channel_permissions(&mut self) -> Override {
Override {
allow: 0,
deny: ChannelPermission::SendMessage as u64,
}
}
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
}
}
}