From 887ddbb4b6e09d3e25728734a961dfcf5ea2d881 Mon Sep 17 00:00:00 2001 From: Rapptz Date: Thu, 29 Dec 2022 07:17:41 -0500 Subject: [PATCH] Fix implicit permission resolution for Thread Fix #9153 --- discord/abc.py | 28 +++++++++++++++------------- discord/channel.py | 3 +++ discord/threads.py | 29 ++++++++++++++++++++++------- 3 files changed, 40 insertions(+), 20 deletions(-) diff --git a/discord/abc.py b/discord/abc.py index 19912a646..d91e3b6c2 100644 --- a/discord/abc.py +++ b/discord/abc.py @@ -639,6 +639,20 @@ class GuildChannel: category = self.guild.get_channel(self.category_id) return bool(category and category.overwrites == self.overwrites) + def _apply_implicit_permissions(self, base: Permissions) -> None: + # if you can't send a message in a channel then you can't have certain + # permissions as well + if not base.send_messages: + base.send_tts_messages = False + base.mention_everyone = False + base.embed_links = False + base.attach_files = False + + # if you can't read a channel then you have no permissions there + if not base.read_messages: + denied = Permissions.all_channel() + base.value &= ~denied.value + def permissions_for(self, obj: Union[Member, Role], /) -> Permissions: """Handles permission resolution for the :class:`~discord.Member` or :class:`~discord.Role`. @@ -649,6 +663,7 @@ class GuildChannel: - Guild roles - Channel overrides - Member overrides + - Implicit permissions - Member timeout If a :class:`~discord.Role` is passed, then it checks the permissions @@ -764,19 +779,6 @@ class GuildChannel: base.handle_overwrite(allow=overwrite.allow, deny=overwrite.deny) break - # if you can't send a message in a channel then you can't have certain - # permissions as well - if not base.send_messages: - base.send_tts_messages = False - base.mention_everyone = False - base.embed_links = False - base.attach_files = False - - # if you can't read a channel then you have no permissions there - if not base.read_messages: - denied = Permissions.all_channel() - base.value &= ~denied.value - if obj.is_timed_out(): # Timeout leads to every permission except VIEW_CHANNEL and READ_MESSAGE_HISTORY # being explicitly denied diff --git a/discord/channel.py b/discord/channel.py index b45af4a0c..05c550dbf 100644 --- a/discord/channel.py +++ b/discord/channel.py @@ -231,6 +231,7 @@ class TextChannel(discord.abc.Messageable, discord.abc.GuildChannel, Hashable): @utils.copy_doc(discord.abc.GuildChannel.permissions_for) def permissions_for(self, obj: Union[Member, Role], /) -> Permissions: base = super().permissions_for(obj) + self._apply_implicit_permissions(base) # text channels do not have voice related permissions denied = Permissions.voice() @@ -975,6 +976,7 @@ class VocalGuildChannel(discord.abc.Connectable, discord.abc.GuildChannel, Hasha @utils.copy_doc(discord.abc.GuildChannel.permissions_for) def permissions_for(self, obj: Union[Member, Role], /) -> Permissions: base = super().permissions_for(obj) + self._apply_implicit_permissions(base) # voice channels cannot be edited by people who can't connect to them # It also implicitly denies all other voice perms @@ -2219,6 +2221,7 @@ class ForumChannel(discord.abc.GuildChannel, Hashable): @utils.copy_doc(discord.abc.GuildChannel.permissions_for) def permissions_for(self, obj: Union[Member, Role], /) -> Permissions: base = super().permissions_for(obj) + self._apply_implicit_permissions(base) # text channels do not have voice related permissions denied = Permissions.voice() diff --git a/discord/threads.py b/discord/threads.py index 72eab5208..c5551a55a 100644 --- a/discord/threads.py +++ b/discord/threads.py @@ -29,10 +29,11 @@ from datetime import datetime import array from .mixins import Hashable -from .abc import Messageable, _purge_helper +from .abc import Messageable, GuildChannel, _purge_helper from .enums import ChannelType, try_enum from .errors import ClientException from .flags import ChannelFlags +from .permissions import Permissions from .utils import MISSING, parse_time, _get_as_snowflake, _unique __all__ = ( @@ -56,7 +57,6 @@ if TYPE_CHECKING: from .message import Message, PartialMessage from .abc import Snowflake, SnowflakeTime from .role import Role - from .permissions import Permissions from .state import ConnectionState ThreadChannelType = Literal[ChannelType.news_thread, ChannelType.public_thread, ChannelType.private_thread] @@ -394,10 +394,9 @@ class Thread(Messageable, Hashable): """Handles permission resolution for the :class:`~discord.Member` or :class:`~discord.Role`. - Since threads do not have their own permissions, they inherit them - from the parent channel. This is a convenience method for - calling :meth:`~discord.TextChannel.permissions_for` on the - parent channel. + Since threads do not have their own permissions, they mostly + inherit them from the parent channel with some implicit + permissions changed. Parameters ---------- @@ -420,7 +419,23 @@ class Thread(Messageable, Hashable): parent = self.parent if parent is None: raise ClientException('Parent channel not found') - return parent.permissions_for(obj) + + base = GuildChannel.permissions_for(parent, obj) + + # if you can't send a message in a channel then you can't have certain + # permissions as well + if not base.send_messages_in_threads: + base.send_tts_messages = False + base.mention_everyone = False + base.embed_links = False + base.attach_files = False + + # if you can't read a channel then you have no permissions there + if not base.read_messages: + denied = Permissions.all_channel() + base.value &= ~denied.value + + return base async def delete_messages(self, messages: Iterable[Snowflake], /, *, reason: Optional[str] = None) -> None: """|coro|