Files
discord.js/src/managers/PermissionOverwriteManager.js
BattleEye 4d86cf4ce0 fix(PermissionOverwriteManager): mutates user (#8282)
Fix PermissionOverwriteManager changing userOrRole

Since it's mutated the original Member object won't be passed to upset and will be seen as invalid if User cache is disabled.

Functions normally even with User cache disabled after the fix.
2022-07-17 19:10:03 +02:00

167 lines
6.5 KiB
JavaScript

'use strict';
const process = require('node:process');
const { Collection } = require('@discordjs/collection');
const CachedManager = require('./CachedManager');
const { TypeError } = require('../errors');
const PermissionOverwrites = require('../structures/PermissionOverwrites');
const { Role } = require('../structures/Role');
const { OverwriteTypes } = require('../util/Constants');
let cacheWarningEmitted = false;
/**
* Manages API methods for guild channel permission overwrites and stores their cache.
* @extends {CachedManager}
*/
class PermissionOverwriteManager extends CachedManager {
constructor(channel, iterable) {
super(channel.client, PermissionOverwrites);
if (!cacheWarningEmitted && this._cache.constructor.name !== 'Collection') {
cacheWarningEmitted = true;
process.emitWarning(
`Overriding the cache handling for ${this.constructor.name} is unsupported and breaks functionality.`,
'UnsupportedCacheOverwriteWarning',
);
}
/**
* The channel of the permission overwrite this manager belongs to
* @type {GuildChannel}
*/
this.channel = channel;
if (iterable) {
for (const item of iterable) {
this._add(item);
}
}
}
/**
* The cache of this Manager
* @type {Collection<Snowflake, PermissionOverwrites>}
* @name PermissionOverwriteManager#cache
*/
_add(data, cache) {
return super._add(data, cache, { extras: [this.channel] });
}
/**
* Replaces the permission overwrites in this channel.
* @param {OverwriteResolvable[]|Collection<Snowflake, OverwriteResolvable>} overwrites
* Permission overwrites the channel gets updated with
* @param {string} [reason] Reason for updating the channel overwrites
* @returns {Promise<GuildChannel>}
* @example
* message.channel.permissionOverwrites.set([
* {
* id: message.author.id,
* deny: [Permissions.FLAGS.VIEW_CHANNEL],
* },
* ], 'Needed to change permissions');
*/
set(overwrites, reason) {
if (!Array.isArray(overwrites) && !(overwrites instanceof Collection)) {
return Promise.reject(
new TypeError('INVALID_TYPE', 'overwrites', 'Array or Collection of Permission Overwrites', true),
);
}
return this.channel.edit({ permissionOverwrites: overwrites, reason });
}
/**
* Extra information about the overwrite
* @typedef {Object} GuildChannelOverwriteOptions
* @property {string} [reason] Reason for creating/editing this overwrite
* @property {number} [type] The type of overwrite, either `0` for a role or `1` for a member. Use this to bypass
* automatic resolution of type that results in an error for uncached structure
*/
/**
* Creates or edits permission overwrites for a user or role in this channel.
* @param {RoleResolvable|UserResolvable} userOrRole The user or role to update
* @param {PermissionOverwriteOptions} options The options for the update
* @param {GuildChannelOverwriteOptions} [overwriteOptions] The extra information for the update
* @param {PermissionOverwrites} [existing] The existing overwrites to merge with this update
* @returns {Promise<GuildChannel>}
* @private
*/
async upsert(userOrRole, options, overwriteOptions = {}, existing) {
let userOrRoleId = this.channel.guild.roles.resolveId(userOrRole) ?? this.client.users.resolveId(userOrRole);
let { type, reason } = overwriteOptions;
if (typeof type !== 'number') {
userOrRole = this.channel.guild.roles.resolve(userOrRole) ?? this.client.users.resolve(userOrRole);
if (!userOrRole) throw new TypeError('INVALID_TYPE', 'parameter', 'User nor a Role');
type = userOrRole instanceof Role ? OverwriteTypes.role : OverwriteTypes.member;
}
const { allow, deny } = PermissionOverwrites.resolveOverwriteOptions(options, existing);
await this.client.api
.channels(this.channel.id)
.permissions(userOrRoleId)
.put({
data: { id: userOrRoleId, type, allow, deny },
reason,
});
return this.channel;
}
/**
* Creates permission overwrites for a user or role in this channel, or replaces them if already present.
* @param {RoleResolvable|UserResolvable} userOrRole The user or role to update
* @param {PermissionOverwriteOptions} options The options for the update
* @param {GuildChannelOverwriteOptions} [overwriteOptions] The extra information for the update
* @returns {Promise<GuildChannel>}
* @example
* // Create or Replace permission overwrites for a message author
* message.channel.permissionOverwrites.create(message.author, {
* SEND_MESSAGES: false
* })
* .then(channel => console.log(channel.permissionOverwrites.cache.get(message.author.id)))
* .catch(console.error);
*/
create(userOrRole, options, overwriteOptions) {
return this.upsert(userOrRole, options, overwriteOptions);
}
/**
* Edits permission overwrites for a user or role in this channel, or creates an entry if not already present.
* @param {RoleResolvable|UserResolvable} userOrRole The user or role to update
* @param {PermissionOverwriteOptions} options The options for the update
* @param {GuildChannelOverwriteOptions} [overwriteOptions] The extra information for the update
* @returns {Promise<GuildChannel>}
* @example
* // Edit or Create permission overwrites for a message author
* message.channel.permissionOverwrites.edit(message.author, {
* SEND_MESSAGES: false
* })
* .then(channel => console.log(channel.permissionOverwrites.cache.get(message.author.id)))
* .catch(console.error);
*/
edit(userOrRole, options, overwriteOptions) {
const existing = this.cache.get(
this.channel.guild.roles.resolveId(userOrRole) ?? this.client.users.resolveId(userOrRole),
);
return this.upsert(userOrRole, options, overwriteOptions, existing);
}
/**
* Deletes permission overwrites for a user or role in this channel.
* @param {UserResolvable|RoleResolvable} userOrRole The user or role to delete
* @param {string} [reason] The reason for deleting the overwrite
* @returns {Promise<GuildChannel>}
*/
async delete(userOrRole, reason) {
const userOrRoleId = this.channel.guild.roles.resolveId(userOrRole) ?? this.client.users.resolveId(userOrRole);
if (!userOrRoleId) throw new TypeError('INVALID_TYPE', 'parameter', 'User nor a Role');
await this.client.api.channels(this.channel.id).permissions(userOrRoleId).delete({ reason });
return this.channel;
}
}
module.exports = PermissionOverwriteManager;