use crate::types::{Channel, Member, Permissions, Role};
pub fn compute_permissions(member: &Member, guild_roles: &[Role], channel: &Channel, owner_id: &str) -> Permissions {
if member.user.as_ref().map(|u| u.id.as_str()) == Some(owner_id) {
return Permissions::all();
}
let everyone_id = channel.guild_id.as_deref().unwrap_or("");
let mut base = Permissions::empty();
for role in guild_roles {
if role.id == everyone_id {
base |= parse_perms(&role.permissions);
}
}
let member_role_ids: std::collections::HashSet<&str> = member.roles.iter().map(String::as_str).collect();
for role in guild_roles {
if member_role_ids.contains(role.id.as_str()) {
base |= parse_perms(&role.permissions);
}
}
if base.contains(Permissions::ADMINISTRATOR) {
return Permissions::all();
}
for ow in &channel.permission_overwrites {
if ow.overwrite_type == 0 && ow.id == everyone_id {
base &= !parse_perms(&ow.deny);
base |= parse_perms(&ow.allow);
}
}
let mut role_allow = Permissions::empty();
let mut role_deny = Permissions::empty();
for ow in &channel.permission_overwrites {
if ow.overwrite_type == 0 && member_role_ids.contains(ow.id.as_str()) {
role_deny |= parse_perms(&ow.deny);
role_allow |= parse_perms(&ow.allow);
}
}
base &= !role_deny;
base |= role_allow;
let member_id = member.user.as_ref().map(|u| u.id.as_str()).unwrap_or("");
for ow in &channel.permission_overwrites {
if ow.overwrite_type == 1 && ow.id == member_id {
base &= !parse_perms(&ow.deny);
base |= parse_perms(&ow.allow);
}
}
base
}
pub fn parse_perms(s: &str) -> Permissions {
if s.is_empty() {
return Permissions::empty();
}
s.parse::<u64>().map(Permissions::from_bits_truncate).unwrap_or(Permissions::empty())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::{Channel, ChannelType, Member, PermissionOverwrite, Permissions, Role, User};
fn make_user(id: &str) -> User {
User {
id: id.to_string(),
username: "test".to_string(),
discriminator: "0000".to_string(),
..Default::default()
}
}
fn make_member(user_id: &str, roles: Vec<String>) -> Member {
Member {
user: Some(make_user(user_id)),
roles,
..Default::default()
}
}
fn make_role(id: &str, permissions: u64) -> Role {
Role {
id: id.to_string(),
name: "test".to_string(),
color: 0,
hoist: false,
icon: None,
unicode_emoji: None,
position: 0,
permissions: permissions.to_string(),
managed: false,
mentionable: false,
flags: 0,
tags: None,
colors: None,
}
}
fn make_channel(guild_id: &str, overwrites: Vec<PermissionOverwrite>) -> Channel {
Channel {
id: "chan1".to_string(),
guild_id: Some(guild_id.to_string()),
channel_type: ChannelType::GuildText,
name: Some("general".to_string()),
position: Some(0),
permission_overwrites: overwrites,
..Default::default()
}
}
#[test]
fn owner_gets_all_permissions() {
let member = make_member("owner1", vec![]);
let roles = vec![make_role("guild1", 0)]; let channel = make_channel("guild1", vec![]);
let perms = compute_permissions(&member, &roles, &channel, "owner1");
assert_eq!(perms, Permissions::all());
}
#[test]
fn base_permissions_from_everyone_role() {
let member = make_member("user1", vec![]);
let send_msgs = Permissions::SEND_MESSAGES.bits();
let roles = vec![make_role("guild1", send_msgs)]; let channel = make_channel("guild1", vec![]);
let perms = compute_permissions(&member, &roles, &channel, "owner1");
assert!(perms.contains(Permissions::SEND_MESSAGES));
}
#[test]
fn channel_overwrite_denies_permission() {
let member = make_member("user1", vec![]);
let send_msgs = Permissions::SEND_MESSAGES.bits();
let roles = vec![make_role("guild1", send_msgs)];
let ow = PermissionOverwrite {
id: "guild1".to_string(), overwrite_type: 0,
allow: "0".to_string(),
deny: send_msgs.to_string(),
};
let channel = make_channel("guild1", vec![ow]);
let perms = compute_permissions(&member, &roles, &channel, "owner1");
assert!(!perms.contains(Permissions::SEND_MESSAGES));
}
#[test]
fn member_overwrite_grants_permission() {
let member = make_member("user1", vec![]);
let roles = vec![make_role("guild1", 0)]; let ow = PermissionOverwrite {
id: "user1".to_string(),
overwrite_type: 1, allow: Permissions::SEND_MESSAGES.bits().to_string(),
deny: "0".to_string(),
};
let channel = make_channel("guild1", vec![ow]);
let perms = compute_permissions(&member, &roles, &channel, "owner1");
assert!(perms.contains(Permissions::SEND_MESSAGES));
}
}