Skip to content

Commit ad58c3f

Browse files
authored
Deprecate permission methods which do not handle overwrites (#3037)
These methods are footguns and shouldn't be trusted, so let's deprecate and remove them... this also fixes the `author_permissions` method which got it's foot blown off.
1 parent 57fdc2f commit ad58c3f

File tree

6 files changed

+94
-4
lines changed

6 files changed

+94
-4
lines changed

src/cache/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ use crate::model::prelude::*;
4646
mod cache_update;
4747
mod event;
4848
mod settings;
49-
mod wrappers;
49+
pub(crate) mod wrappers;
5050

5151
#[cfg(feature = "temp_cache")]
5252
pub(crate) use wrappers::MaybeOwnedArc;

src/cache/wrappers.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use typesize::TypeSize;
1212

1313
#[derive(Debug)]
1414
/// A wrapper around Option<DashMap<K, V>> to ease disabling specific cache fields.
15-
pub(crate) struct MaybeMap<K: Eq + Hash, V>(pub(super) Option<DashMap<K, V, BuildHasher>>);
15+
pub(crate) struct MaybeMap<K: Eq + Hash, V>(pub(crate) Option<DashMap<K, V, BuildHasher>>);
1616
impl<K: Eq + Hash, V> MaybeMap<K, V> {
1717
pub fn iter(&self) -> impl Iterator<Item = RefMulti<'_, K, V, BuildHasher>> {
1818
Option::iter(&self.0).flat_map(DashMap::iter)

src/model/channel/message.rs

+83-2
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ impl Message {
220220
///
221221
/// This may return `None` if:
222222
/// - The [`Cache`] does not have the current [`Guild`]
223+
/// - The [`Guild`] does not have the current channel cached (should never happen).
223224
/// - This message is not from [`MessageCreateEvent`] and the author's [`Member`] cannot be
224225
/// found in [`Guild#structfield.members`].
225226
#[cfg(feature = "cache")]
@@ -229,10 +230,18 @@ impl Message {
229230
};
230231

231232
let guild = cache.as_ref().guild(guild_id)?;
233+
let channel = if let Some(channel) = guild.channels.get(&self.channel_id) {
234+
channel
235+
} else if let Some(thread) = guild.threads.iter().find(|th| th.id == self.channel_id) {
236+
thread
237+
} else {
238+
return None;
239+
};
240+
232241
if let Some(member) = &self.member {
233-
Some(guild.partial_member_permissions(self.author.id, member))
242+
Some(guild.partial_member_permissions_in(channel, self.author.id, member))
234243
} else {
235-
Some(guild.member_permissions(guild.members.get(&self.author.id)?))
244+
Some(guild.user_permissions_in(channel, guild.members.get(&self.author.id)?))
236245
}
237246
}
238247

@@ -1443,3 +1452,75 @@ pub struct PollAnswerCount {
14431452
pub count: u64,
14441453
pub me_voted: bool,
14451454
}
1455+
1456+
// all tests here require cache, move if non-cache test is added
1457+
#[cfg(all(test, feature = "cache"))]
1458+
mod tests {
1459+
use std::collections::HashMap;
1460+
1461+
use dashmap::DashMap;
1462+
1463+
use super::{
1464+
Guild,
1465+
GuildChannel,
1466+
Member,
1467+
Message,
1468+
PermissionOverwrite,
1469+
PermissionOverwriteType,
1470+
Permissions,
1471+
User,
1472+
UserId,
1473+
};
1474+
use crate::cache::wrappers::MaybeMap;
1475+
use crate::cache::Cache;
1476+
1477+
/// Test that author_permissions checks the permissions in a channel, not just the guild.
1478+
#[test]
1479+
fn author_permissions_respects_overwrites() {
1480+
// Author of the message, with a random ID that won't collide with defaults.
1481+
let author = User {
1482+
id: UserId::new(50778944701071),
1483+
..Default::default()
1484+
};
1485+
1486+
// Channel with the message, with SEND_MESSAGES on.
1487+
let channel = GuildChannel {
1488+
permission_overwrites: vec![PermissionOverwrite {
1489+
allow: Permissions::SEND_MESSAGES,
1490+
deny: Permissions::default(),
1491+
kind: PermissionOverwriteType::Member(author.id),
1492+
}],
1493+
..Default::default()
1494+
};
1495+
let channel_id = channel.id;
1496+
1497+
// Guild with the author and channel cached, default (empty) permissions.
1498+
let guild = Guild {
1499+
channels: HashMap::from([(channel.id, channel)]),
1500+
members: HashMap::from([(author.id, Member {
1501+
user: author.clone(),
1502+
..Default::default()
1503+
})]),
1504+
..Default::default()
1505+
};
1506+
1507+
// Message, tied to the guild and the channel.
1508+
let message = Message {
1509+
author,
1510+
channel_id,
1511+
guild_id: Some(guild.id),
1512+
..Default::default()
1513+
};
1514+
1515+
// Cache, with the guild setup.
1516+
let mut cache = Cache::new();
1517+
cache.guilds = MaybeMap(Some({
1518+
let guilds = DashMap::default();
1519+
guilds.insert(guild.id, guild);
1520+
guilds
1521+
}));
1522+
1523+
// The author should only have the one permission, SEND_MESSAGES.
1524+
assert_eq!(message.author_permissions(&cache), Some(Permissions::SEND_MESSAGES));
1525+
}
1526+
}

src/model/guild/member.rs

+3
Original file line numberDiff line numberDiff line change
@@ -435,8 +435,11 @@ impl Member {
435435
/// And/or returns [`ModelError::ItemMissing`] if the "default channel" of the guild is not
436436
/// found.
437437
#[cfg(feature = "cache")]
438+
#[deprecated = "Use Guild::member_permissions_in, as this doesn't consider permission overwrites"]
438439
pub fn permissions(&self, cache: impl AsRef<Cache>) -> Result<Permissions> {
439440
let guild = cache.as_ref().guild(self.guild_id).ok_or(ModelError::GuildNotFound)?;
441+
442+
#[allow(deprecated)]
440443
Ok(guild.member_permissions(self))
441444
}
442445

src/model/guild/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,8 @@ impl Guild {
428428
required_permissions: Permissions,
429429
) -> Result<(), Error> {
430430
if let Some(member) = self.members.get(&cache.current_user().id) {
431+
// This isn't used for any channel-specific permissions, but sucks still.
432+
#[allow(deprecated)]
431433
let bot_permissions = self.member_permissions(member);
432434
if !bot_permissions.contains(required_permissions) {
433435
return Err(Error::Model(ModelError::InvalidPermissions {
@@ -1908,6 +1910,7 @@ impl Guild {
19081910
/// Calculate a [`Member`]'s permissions in the guild.
19091911
#[inline]
19101912
#[must_use]
1913+
#[deprecated = "Use Guild::member_permissions_in, as this doesn't consider permission overwrites"]
19111914
pub fn member_permissions(&self, member: &Member) -> Permissions {
19121915
Self::user_permissions_in_(
19131916
None,
@@ -1926,6 +1929,7 @@ impl Guild {
19261929
/// Panics if the passed [`UserId`] does not match the [`PartialMember`] id, if user is Some.
19271930
#[inline]
19281931
#[must_use]
1932+
#[deprecated = "Use Guild::partial_member_permissions_in, as this doesn't consider permission overwrites"]
19291933
pub fn partial_member_permissions(
19301934
&self,
19311935
member_id: UserId,

src/model/guild/partial_guild.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1035,6 +1035,7 @@ impl PartialGuild {
10351035
/// Calculate a [`Member`]'s permissions in the guild.
10361036
#[inline]
10371037
#[must_use]
1038+
#[deprecated = "Use PartialGuild::member_permissions_in, as this doesn't consider permission overwrites"]
10381039
pub fn member_permissions(&self, member: &Member) -> Permissions {
10391040
Guild::user_permissions_in_(
10401041
None,
@@ -1053,6 +1054,7 @@ impl PartialGuild {
10531054
/// Panics if the passed [`UserId`] does not match the [`PartialMember`] id, if user is Some.
10541055
#[inline]
10551056
#[must_use]
1057+
#[deprecated = "Use PartialGuild::partial_member_permissions_in, as this doesn't consider permission overwrites"]
10561058
pub fn partial_member_permissions(
10571059
&self,
10581060
member_id: UserId,

0 commit comments

Comments
 (0)