Repository: Cog-Creators/Red-DiscordBot

Issue:
[Core] Users may accidentally lock guild owner and themselves with `[p]localwhitelist add`
# Command bugs

#### Command name

`[p]localwhitelist add`

#### What cog is this command from?

Core

#### What were you expecting to happen?

I was expecting to get an error saying that I can't lock out myself (and guild owner) from using the bot.

#### What actually happened?

I locked myself and guild owner out.

#### How can we reproduce this issue?

1. Have permissions to use `[p]localwhitelist` (e.g. have admin role), but don't be bot owner.
2. Use `[p]localwhitelist clear`
3. Use `[p]localwhitelist add SOMEONE_ELSES_USER_ID`

---

This doesn't seem like a trivial task but it would surely be great to have it improved.



Discussion:
We can't escape this case in any good way at the command level. We can do like with bot owner and the global list, and at least exclude the guild owner from being checked by local whitelist/blacklist so that there's always at least 1 authoritative person with a way to fix that specific issue.

While it would prevent a bot owner from localblacklisting a guild owner, I'd argue the bot owner has more powerful tools such as blacklisting the entire server, leaving a server, or the global bot settings. for blacklisting
~~This issues has been in triage status for a while, and it really needs some fix.~~

~~PRs excluding guild owner from local whitelist/blacklist checks are very much welcome.~~

Looks like the important (the more trivial) part of this issue has already been fixed.

It would be awesome if the commands were "smart" and knew whether you would lock yourself out when running them but it's not trivial and since there is at least one person in the guild who can still fix the localwhitelist problem, it isn't a priority. 


Looks like the part that badly needed the fix was already fixed around the time this issue was created. I updated my previous comment to reflect this.

Resolving patch:
diff --git a/redbot/core/core_commands.py b/redbot/core/core_commands.py
--- a/redbot/core/core_commands.py
+++ b/redbot/core/core_commands.py
@@ -1946,8 +1946,20 @@ async def localwhitelist_add(
             return
 
         names = [getattr(u_or_r, "name", u_or_r) for u_or_r in users_or_roles]
-        uids = [getattr(u_or_r, "id", u_or_r) for u_or_r in users_or_roles]
-        await self.bot._whiteblacklist_cache.add_to_whitelist(ctx.guild, uids)
+        uids = {getattr(u_or_r, "id", u_or_r) for u_or_r in users_or_roles}
+        if not (ctx.guild.owner == ctx.author or await self.bot.is_owner(ctx.author)):
+            current_whitelist = set(await self.bot._whiteblacklist_cache.get_whitelist(ctx.guild))
+            theoretical_whitelist = current_whitelist.union(uids)
+            ids = {i for i in (ctx.author.id, *(getattr(ctx.author, "_roles", [])))}
+            if ids.isdisjoint(theoretical_whitelist):
+                return await ctx.send(
+                    _(
+                        "I cannot allow you to do this, as it would "
+                        "remove your ability to run commands, "
+                        "please ensure to add yourself to the whitelist first."
+                    )
+                )
+        await self.bot._whiteblacklist_cache.add_to_whitelist(ctx.guild, list(uids))
 
         await ctx.send(_("{names} added to whitelist.").format(names=humanize_list(names)))
 
@@ -1981,8 +1993,19 @@ async def localwhitelist_remove(
             return
 
         names = [getattr(u_or_r, "name", u_or_r) for u_or_r in users_or_roles]
-        uids = [getattr(u_or_r, "id", u_or_r) for u_or_r in users_or_roles]
-        await self.bot._whiteblacklist_cache.remove_from_whitelist(ctx.guild, uids)
+        uids = {getattr(u_or_r, "id", u_or_r) for u_or_r in users_or_roles}
+        if not (ctx.guild.owner == ctx.author or await self.bot.is_owner(ctx.author)):
+            current_whitelist = set(await self.bot._whiteblacklist_cache.get_whitelist(ctx.guild))
+            theoretical_whitelist = current_whitelist - uids
+            ids = {i for i in (ctx.author.id, *(getattr(ctx.author, "_roles", [])))}
+            if theoretical_whitelist and ids.isdisjoint(theoretical_whitelist):
+                return await ctx.send(
+                    _(
+                        "I cannot allow you to do this, as it would "
+                        "remove your ability to run commands."
+                    )
+                )
+        await self.bot._whiteblacklist_cache.remove_from_whitelist(ctx.guild, list(uids))
 
         await ctx.send(
             _("{names} removed from the local whitelist.").format(names=humanize_list(names))


Test patch: