Implement channel permissions resolution (#11)
* Add channel permission resolution * Fix target ID handling in permission overwrite retrieval
This commit is contained in:
parent
c6fb120449
commit
484f091897
@ -21,6 +21,7 @@ from .enums import ( # These enums will need to be defined in disagreement/enum
|
||||
ButtonStyle, # Added for Button
|
||||
# SelectMenuType will be part of ComponentType or a new enum if needed
|
||||
)
|
||||
from .permissions import Permissions
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@ -897,6 +898,78 @@ class Channel:
|
||||
def __repr__(self) -> str:
|
||||
return f"<Channel id='{self.id}' name='{self.name}' type='{self.type.name if hasattr(self.type, 'name') else self._type_val}'>"
|
||||
|
||||
def permission_overwrite_for(
|
||||
self, target: Union["Role", "Member", str]
|
||||
) -> Optional["PermissionOverwrite"]:
|
||||
"""Return the :class:`PermissionOverwrite` for ``target`` if present."""
|
||||
|
||||
if isinstance(target, str):
|
||||
target_id = int(target)
|
||||
else:
|
||||
target_id = target.id
|
||||
for overwrite in self.permission_overwrites:
|
||||
if overwrite.id == target_id:
|
||||
return overwrite
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def _apply_overwrite(
|
||||
perms: Permissions, overwrite: Optional["PermissionOverwrite"]
|
||||
) -> Permissions:
|
||||
if overwrite is None:
|
||||
return perms
|
||||
|
||||
perms &= ~Permissions(int(overwrite.deny))
|
||||
perms |= Permissions(int(overwrite.allow))
|
||||
return perms
|
||||
|
||||
def permissions_for(self, member: "Member") -> Permissions:
|
||||
"""Resolve channel permissions for ``member``."""
|
||||
|
||||
if self.guild_id is None:
|
||||
return Permissions(~0)
|
||||
|
||||
if not hasattr(self._client, "get_guild"):
|
||||
return Permissions(0)
|
||||
|
||||
guild = self._client.get_guild(self.guild_id)
|
||||
if guild is None:
|
||||
return Permissions(0)
|
||||
|
||||
base = Permissions(0)
|
||||
|
||||
everyone = guild.get_role(guild.id)
|
||||
if everyone is not None:
|
||||
base |= Permissions(int(everyone.permissions))
|
||||
|
||||
for rid in member.roles:
|
||||
role = guild.get_role(rid)
|
||||
if role is not None:
|
||||
base |= Permissions(int(role.permissions))
|
||||
|
||||
if base & Permissions.ADMINISTRATOR:
|
||||
return Permissions(~0)
|
||||
|
||||
# Apply @everyone overwrite
|
||||
base = self._apply_overwrite(base, self.permission_overwrite_for(guild.id))
|
||||
|
||||
# Role overwrites
|
||||
role_allow = Permissions(0)
|
||||
role_deny = Permissions(0)
|
||||
for rid in member.roles:
|
||||
ow = self.permission_overwrite_for(rid)
|
||||
if ow is not None:
|
||||
role_allow |= Permissions(int(ow.allow))
|
||||
role_deny |= Permissions(int(ow.deny))
|
||||
|
||||
base &= ~role_deny
|
||||
base |= role_allow
|
||||
|
||||
# Member overwrite
|
||||
base = self._apply_overwrite(base, self.permission_overwrite_for(member.id))
|
||||
|
||||
return base
|
||||
|
||||
|
||||
class TextChannel(Channel):
|
||||
"""Represents a guild text channel or announcement channel."""
|
||||
|
128
tests/test_channel_permissions.py
Normal file
128
tests/test_channel_permissions.py
Normal file
@ -0,0 +1,128 @@
|
||||
import pytest # pylint: disable=E0401
|
||||
|
||||
from disagreement.models import Guild, Member, Role, TextChannel, PermissionOverwrite
|
||||
from disagreement.enums import (
|
||||
ChannelType,
|
||||
VerificationLevel,
|
||||
MessageNotificationLevel,
|
||||
ExplicitContentFilterLevel,
|
||||
MFALevel,
|
||||
GuildNSFWLevel,
|
||||
PremiumTier,
|
||||
OverwriteType,
|
||||
)
|
||||
from disagreement.permissions import Permissions
|
||||
|
||||
|
||||
class DummyClient:
|
||||
def __init__(self):
|
||||
self._guilds = {}
|
||||
|
||||
def get_guild(self, gid):
|
||||
return self._guilds.get(gid)
|
||||
|
||||
|
||||
def _base_guild(client):
|
||||
data = {
|
||||
"id": "1",
|
||||
"name": "g",
|
||||
"owner_id": "1",
|
||||
"afk_timeout": 60,
|
||||
"verification_level": VerificationLevel.NONE.value,
|
||||
"default_message_notifications": MessageNotificationLevel.ALL_MESSAGES.value,
|
||||
"explicit_content_filter": ExplicitContentFilterLevel.DISABLED.value,
|
||||
"roles": [],
|
||||
"emojis": [],
|
||||
"features": [],
|
||||
"mfa_level": MFALevel.NONE.value,
|
||||
"system_channel_flags": 0,
|
||||
"premium_tier": PremiumTier.NONE.value,
|
||||
"nsfw_level": GuildNSFWLevel.DEFAULT.value,
|
||||
}
|
||||
guild = Guild(data, client_instance=client)
|
||||
client._guilds[guild.id] = guild
|
||||
return guild
|
||||
|
||||
|
||||
def _member(guild, *roles):
|
||||
data = {
|
||||
"user": {"id": "10", "username": "u", "discriminator": "0001"},
|
||||
"joined_at": "t",
|
||||
"roles": [r.id for r in roles] or [guild.id],
|
||||
}
|
||||
member = Member(data, client_instance=None)
|
||||
member.guild_id = guild.id
|
||||
guild._members[member.id] = member
|
||||
return member
|
||||
|
||||
|
||||
def _role(guild, rid, perms):
|
||||
role = Role(
|
||||
{
|
||||
"id": rid,
|
||||
"name": f"r{rid}",
|
||||
"color": 0,
|
||||
"hoist": False,
|
||||
"position": 0,
|
||||
"permissions": str(int(perms)),
|
||||
"managed": False,
|
||||
"mentionable": False,
|
||||
}
|
||||
)
|
||||
guild.roles.append(role)
|
||||
return role
|
||||
|
||||
|
||||
def _channel(guild, client):
|
||||
data = {
|
||||
"id": "100",
|
||||
"type": ChannelType.GUILD_TEXT.value,
|
||||
"guild_id": guild.id,
|
||||
"permission_overwrites": [],
|
||||
}
|
||||
channel = TextChannel(data, client_instance=client)
|
||||
guild._channels[channel.id] = channel
|
||||
return channel
|
||||
|
||||
|
||||
def test_permissions_for_base_roles():
|
||||
client = DummyClient()
|
||||
guild = _base_guild(client)
|
||||
everyone = _role(
|
||||
guild, guild.id, Permissions.VIEW_CHANNEL | Permissions.SEND_MESSAGES
|
||||
)
|
||||
mod = _role(guild, "2", Permissions.MANAGE_MESSAGES)
|
||||
member = _member(guild, everyone, mod)
|
||||
channel = _channel(guild, client)
|
||||
|
||||
perms = channel.permissions_for(member)
|
||||
assert perms & Permissions.MANAGE_MESSAGES
|
||||
assert perms & Permissions.SEND_MESSAGES
|
||||
assert perms & Permissions.VIEW_CHANNEL
|
||||
|
||||
|
||||
def test_permissions_for_with_overwrite():
|
||||
client = DummyClient()
|
||||
guild = _base_guild(client)
|
||||
everyone = _role(
|
||||
guild, guild.id, Permissions.VIEW_CHANNEL | Permissions.SEND_MESSAGES
|
||||
)
|
||||
mod = _role(guild, "2", Permissions.MANAGE_MESSAGES)
|
||||
member = _member(guild, everyone, mod)
|
||||
channel = _channel(guild, client)
|
||||
|
||||
channel.permission_overwrites.append(
|
||||
PermissionOverwrite(
|
||||
{
|
||||
"id": mod.id,
|
||||
"type": OverwriteType.ROLE.value,
|
||||
"allow": "0",
|
||||
"deny": str(int(Permissions.MANAGE_MESSAGES)),
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
perms = channel.permissions_for(member)
|
||||
assert not perms & Permissions.MANAGE_MESSAGES
|
||||
assert perms & Permissions.SEND_MESSAGES
|
||||
assert perms & Permissions.VIEW_CHANNEL
|
Loading…
x
Reference in New Issue
Block a user