mirror of
https://github.com/anope/anope.git
synced 2026-06-12 19:14:47 +02:00
cs_entrymsg: Check for the correct override privilege.
Make some more commands check if Read-Only mode is active. cs_flags: Show a meaningful message when a user is not found on the access list. os_set: Add missing capabilities to the readonly help output. OperServ: Add logging to certain commands. NickServ: Undo logging for listings.
This commit is contained in:
+1
-1
@@ -793,7 +793,7 @@ log
|
||||
* memoserv/info - Can see any information with /memoserv info
|
||||
* memoserv/set-limit - Can set the limit of max stored memos on any user and channel
|
||||
* memoserv/no-limit - Can send memos through limits and throttles
|
||||
* nickserv/access - Can modify other users access and certificate list
|
||||
* nickserv/access - Can modify other users access and certificate lists
|
||||
* nickserv/alist - Can see the channel access list of other users
|
||||
* nickserv/auspex - Can see any information with /nickserv info
|
||||
* nickserv/confirm - Can confirm other users nicknames
|
||||
|
||||
@@ -209,7 +209,7 @@ class CommandEntryMessage : public Command
|
||||
return;
|
||||
}
|
||||
|
||||
if (!source.IsFounder(ci) && !source.HasCommand("chanserv/set"))
|
||||
if (!source.IsFounder(ci) && !source.HasPriv("chanserv/administration"))
|
||||
{
|
||||
source.Reply(ACCESS_DENIED);
|
||||
return;
|
||||
|
||||
@@ -222,7 +222,7 @@ class CommandCSFlags : public Command
|
||||
}
|
||||
else
|
||||
{
|
||||
source.Reply(_("Insufficient flags given."));
|
||||
source.Reply(_("\002%s\002 not found on %s access list."), mask.c_str(), ci->name.c_str());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -170,6 +170,12 @@ class CommandCSSetPrivate : public Command
|
||||
|
||||
void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override
|
||||
{
|
||||
if (Anope::ReadOnly)
|
||||
{
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
ChannelInfo *ci = ChannelInfo::Find(params[0]);
|
||||
if (ci == NULL)
|
||||
{
|
||||
|
||||
@@ -78,8 +78,6 @@ class CommandNSAccess : public Command
|
||||
{
|
||||
unsigned i, end;
|
||||
|
||||
Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to view the access list for " << nc->display;
|
||||
|
||||
if (nc->access.empty())
|
||||
{
|
||||
source.Reply(_("%s's access list is empty."), nc->display.c_str());
|
||||
|
||||
@@ -92,8 +92,6 @@ class CommandNSAJoin : public Command
|
||||
{
|
||||
AJoinList *channels = nc->Require<AJoinList>("ajoinlist");
|
||||
|
||||
Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to view the auto join list for " << nc->display;
|
||||
|
||||
if ((*channels)->empty())
|
||||
source.Reply(_("%s's auto join list is empty."), nc->display.c_str());
|
||||
else
|
||||
|
||||
@@ -42,8 +42,6 @@ class CommandNSAList : public Command
|
||||
nc = na->nc;
|
||||
}
|
||||
|
||||
Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to view the channel access list for " << nc->display;
|
||||
|
||||
ListFormatter list(source.GetAccount());
|
||||
int chan_count = 0;
|
||||
|
||||
|
||||
@@ -203,8 +203,6 @@ class CommandNSCert : public Command
|
||||
{
|
||||
NSCertList *cl = nc->GetExt<NSCertList>("certificates");
|
||||
|
||||
Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to view the certificate fingerprint list for " << nc->display;
|
||||
|
||||
if (!cl || !cl->GetCertCount())
|
||||
{
|
||||
source.Reply(_("%s's certificate list is empty."), nc->display.c_str());
|
||||
|
||||
@@ -135,6 +135,12 @@ class CommandNSSetHide : public Command
|
||||
|
||||
void Run(CommandSource &source, const Anope::string &user, const Anope::string ¶m, const Anope::string &arg)
|
||||
{
|
||||
if (Anope::ReadOnly)
|
||||
{
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
const NickAlias *na = NickAlias::Find(user);
|
||||
if (!na)
|
||||
{
|
||||
|
||||
@@ -189,6 +189,12 @@ class CommandNSSetPrivate : public Command
|
||||
|
||||
void Run(CommandSource &source, const Anope::string &user, const Anope::string ¶m)
|
||||
{
|
||||
if (Anope::ReadOnly)
|
||||
{
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
const NickAlias *na = NickAlias::Find(user);
|
||||
if (!na)
|
||||
{
|
||||
|
||||
@@ -163,6 +163,12 @@ class CommandNSSASetPassword : public Command
|
||||
|
||||
void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override
|
||||
{
|
||||
if (Anope::ReadOnly)
|
||||
{
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
const NickAlias *setter_na = NickAlias::Find(params[0]);
|
||||
if (setter_na == NULL)
|
||||
{
|
||||
@@ -1057,6 +1063,12 @@ class CommandNSSASetNoexpire : public Command
|
||||
|
||||
void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override
|
||||
{
|
||||
if (Anope::ReadOnly)
|
||||
{
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
NickAlias *na = NickAlias::Find(params[0]);
|
||||
if (na == NULL)
|
||||
{
|
||||
|
||||
@@ -193,7 +193,7 @@ class CommandOSAKill : public Command
|
||||
|
||||
source.Reply(_("\002%s\002 added to the AKILL list."), mask.c_str());
|
||||
|
||||
Log(LOG_ADMIN, source, this) << "on " << mask << " (" << x->reason << ") expires in " << (expires ? Anope::Duration(expires - Anope::CurTime) : "never") << " [affects " << affected << " user(s) (" << percent << "%)]";
|
||||
Log(LOG_ADMIN, source, this) << "on " << mask << " (" << x->reason << "), expires in " << (expires ? Anope::Duration(expires - Anope::CurTime) : "never") << " [affects " << affected << " user(s) (" << percent << "%)]";
|
||||
if (Anope::ReadOnly)
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
}
|
||||
@@ -233,6 +233,7 @@ class CommandOSAKill : public Command
|
||||
{
|
||||
FOREACH_MOD(OnDelXLine, (source, x, akills));
|
||||
|
||||
Log(LOG_ADMIN, source, this) << "to remove " << x->mask << " from the list";
|
||||
source.Reply(_("\002%s\002 deleted from the AKILL list."), x->mask.c_str());
|
||||
AkillDelCallback::DoDel(source, x);
|
||||
}
|
||||
@@ -358,7 +359,11 @@ class CommandOSAKill : public Command
|
||||
akills->DelXLine(x);
|
||||
}
|
||||
|
||||
Log(LOG_ADMIN, source, this) << "to CLEAR the list";
|
||||
source.Reply(_("The AKILL list has been cleared."));
|
||||
|
||||
if (Anope::ReadOnly)
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
}
|
||||
public:
|
||||
CommandOSAKill(Module *creator) : Command(creator, "operserv/akill", 1, 2)
|
||||
|
||||
@@ -289,6 +289,9 @@ class CommandOSDNS : public Command
|
||||
return;
|
||||
}
|
||||
|
||||
if (Anope::ReadOnly)
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
|
||||
Log(LOG_ADMIN, source, this) << "to add zone " << zone;
|
||||
|
||||
new DNSZone(zone);
|
||||
@@ -306,6 +309,9 @@ class CommandOSDNS : public Command
|
||||
return;
|
||||
}
|
||||
|
||||
if (Anope::ReadOnly)
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
|
||||
Log(LOG_ADMIN, source, this) << "to delete zone " << z->name;
|
||||
|
||||
for (std::set<Anope::string, ci::less>::iterator it = z->servers.begin(), it_end = z->servers.end(); it != it_end; ++it)
|
||||
@@ -344,6 +350,9 @@ class CommandOSDNS : public Command
|
||||
return;
|
||||
}
|
||||
|
||||
if (Anope::ReadOnly)
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
|
||||
z->servers.insert(s->GetName());
|
||||
s->zones.insert(zone);
|
||||
|
||||
@@ -365,6 +374,9 @@ class CommandOSDNS : public Command
|
||||
s = new DNSServer(params[1]);
|
||||
if (zone.empty())
|
||||
{
|
||||
if (Anope::ReadOnly)
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
|
||||
Log(LOG_ADMIN, source, this) << "to add server " << s->GetName();
|
||||
source.Reply(_("Added server %s."), s->GetName().c_str());
|
||||
}
|
||||
@@ -378,6 +390,9 @@ class CommandOSDNS : public Command
|
||||
return;
|
||||
}
|
||||
|
||||
if (Anope::ReadOnly)
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
|
||||
Log(LOG_ADMIN, source, this) << "to add server " << s->GetName() << " to zone " << zone;
|
||||
|
||||
z->servers.insert(s->GetName());
|
||||
@@ -409,6 +424,9 @@ class CommandOSDNS : public Command
|
||||
return;
|
||||
}
|
||||
|
||||
if (Anope::ReadOnly)
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
|
||||
Log(LOG_ADMIN, source, this) << "to remove server " << s->GetName() << " from zone " << z->name;
|
||||
|
||||
z->servers.erase(s->GetName());
|
||||
@@ -428,6 +446,9 @@ class CommandOSDNS : public Command
|
||||
z->servers.erase(s->GetName());
|
||||
}
|
||||
|
||||
if (Anope::ReadOnly)
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
|
||||
Log(LOG_ADMIN, source, this) << "to delete server " << s->GetName();
|
||||
source.Reply(_("Removed server %s."), s->GetName().c_str());
|
||||
delete s;
|
||||
@@ -457,6 +478,9 @@ class CommandOSDNS : public Command
|
||||
return;
|
||||
}
|
||||
|
||||
if (Anope::ReadOnly)
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
|
||||
s->GetIPs().push_back(params[2]);
|
||||
source.Reply(_("Added IP %s to %s."), params[2].c_str(), s->GetName().c_str());
|
||||
Log(LOG_ADMIN, source, this) << "to add IP " << params[2] << " to " << s->GetName();
|
||||
@@ -479,6 +503,9 @@ class CommandOSDNS : public Command
|
||||
return;
|
||||
}
|
||||
|
||||
if (Anope::ReadOnly)
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
|
||||
for (unsigned i = 0; i < s->GetIPs().size(); ++i)
|
||||
if (params[2].equals_ci(s->GetIPs()[i]))
|
||||
{
|
||||
@@ -515,6 +542,9 @@ class CommandOSDNS : public Command
|
||||
return;
|
||||
}
|
||||
|
||||
if (Anope::ReadOnly)
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
|
||||
if (params[2].equals_ci("LIMIT"))
|
||||
{
|
||||
try
|
||||
@@ -560,6 +590,9 @@ class CommandOSDNS : public Command
|
||||
return;
|
||||
}
|
||||
|
||||
if (Anope::ReadOnly)
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
|
||||
s->SetActive(true);
|
||||
|
||||
source.Reply(_("Pooled %s."), s->GetName().c_str());
|
||||
@@ -582,6 +615,9 @@ class CommandOSDNS : public Command
|
||||
return;
|
||||
}
|
||||
|
||||
if (Anope::ReadOnly)
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
|
||||
s->Pool(false);
|
||||
|
||||
source.Reply(_("Depooled %s."), s->GetName().c_str());
|
||||
|
||||
@@ -170,6 +170,9 @@ class CommandOSForbid : public Command
|
||||
if (created)
|
||||
this->fs->AddForbid(d);
|
||||
|
||||
if (Anope::ReadOnly)
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
|
||||
Log(LOG_ADMIN, source, this) << "to add a forbid on " << entry << " of type " << subcommand;
|
||||
source.Reply(_("Added a forbid on %s to expire on %s."), entry.c_str(), d->expires ? Anope::strftime(d->expires, source.GetAccount()).c_str() : "never");
|
||||
|
||||
@@ -272,6 +275,9 @@ class CommandOSForbid : public Command
|
||||
ForbidData *d = this->fs->FindForbid(entry, ftype);
|
||||
if (d != NULL)
|
||||
{
|
||||
if (Anope::ReadOnly)
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
|
||||
Log(LOG_ADMIN, source, this) << "to remove forbid on " << d->mask << " of type " << subcommand;
|
||||
source.Reply(_("%s deleted from the %s forbid list."), d->mask.c_str(), subcommand.c_str());
|
||||
this->fs->RemoveForbid(d);
|
||||
|
||||
@@ -180,6 +180,9 @@ class CommandOSIgnore : public Command
|
||||
return;
|
||||
}
|
||||
|
||||
if (Anope::ReadOnly)
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
|
||||
ignore_service->AddIgnore(mask, source.GetNick(), reason, t);
|
||||
if (!t)
|
||||
{
|
||||
@@ -263,6 +266,9 @@ class CommandOSIgnore : public Command
|
||||
|
||||
if (ignore_service->DelIgnore(mask))
|
||||
{
|
||||
if (Anope::ReadOnly)
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
|
||||
Log(LOG_ADMIN, source, this) << "to remove an ignore on " << mask;
|
||||
source.Reply(_("\002%s\002 will no longer be ignored."), mask.c_str());
|
||||
}
|
||||
@@ -275,7 +281,11 @@ class CommandOSIgnore : public Command
|
||||
if (!ignore_service)
|
||||
return;
|
||||
|
||||
if (Anope::ReadOnly)
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
|
||||
ignore_service->ClearIgnores();
|
||||
Log(LOG_ADMIN, source, this) << "to CLEAR the list";
|
||||
source.Reply(_("Ignore list has been cleared."));
|
||||
|
||||
return;
|
||||
|
||||
@@ -163,6 +163,9 @@ class CommandOSInfo : public Command
|
||||
|
||||
source.Reply(_("Added info to \002%s\002."), target.c_str());
|
||||
Log(LOG_ADMIN, source, this) << "to add information to " << target;
|
||||
|
||||
if (Anope::ReadOnly)
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
}
|
||||
else if (cmd.equals_ci("DEL"))
|
||||
{
|
||||
@@ -204,6 +207,9 @@ class CommandOSInfo : public Command
|
||||
|
||||
source.Reply(_("Deleted info from \002%s\002."), target.c_str());
|
||||
Log(LOG_ADMIN, source, this) << "to remove information from " << target;
|
||||
|
||||
if (Anope::ReadOnly)
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
}
|
||||
}
|
||||
else if (cmd.equals_ci("CLEAR"))
|
||||
@@ -220,6 +226,9 @@ class CommandOSInfo : public Command
|
||||
|
||||
source.Reply(_("Cleared info from \002%s\002."), target.c_str());
|
||||
Log(LOG_ADMIN, source, this) << "to clear information for " << target;
|
||||
|
||||
if (Anope::ReadOnly)
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -265,11 +265,11 @@ class CommandOSLogonNews : public NewsBase
|
||||
this->SendSyntax(source);
|
||||
source.Reply(" ");
|
||||
source.Reply(_("Edits or displays the list of logon news messages. When a\n"
|
||||
"user connects to the network, these messages will be sent\n"
|
||||
"to them. However, no more than \002%d\002 messages will be\n"
|
||||
"sent in order to avoid flooding the user. If there are\n"
|
||||
"more news messages, only the most recent will be sent."),
|
||||
Config->GetModule(this->owner)->Get<unsigned>("newscount", "3"));
|
||||
"user connects to the network, these messages will be sent\n"
|
||||
"to them. However, no more than \002%d\002 messages will be\n"
|
||||
"sent in order to avoid flooding the user. If there are\n"
|
||||
"more news messages, only the most recent will be sent."),
|
||||
Config->GetModule(this->owner)->Get<unsigned>("newscount", "3"));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -104,6 +104,9 @@ class CommandOSOper : public Command
|
||||
|
||||
na->nc->o = new MyOper(na->nc->display, ot);
|
||||
|
||||
if (Anope::ReadOnly)
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
|
||||
Log(LOG_ADMIN, source, this) << "ADD " << na->nick << " as type " << ot->GetName();
|
||||
source.Reply("%s (%s) added to the \002%s\002 list.", na->nick.c_str(), na->nc->display.c_str(), ot->GetName().c_str());
|
||||
}
|
||||
@@ -124,6 +127,9 @@ class CommandOSOper : public Command
|
||||
delete na->nc->o;
|
||||
na->nc->o = NULL;
|
||||
|
||||
if (Anope::ReadOnly)
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
|
||||
Log(LOG_ADMIN, source, this) << "DEL " << na->nick;
|
||||
source.Reply(_("Oper privileges removed from %s (%s)."), na->nick.c_str(), na->nc->display.c_str());
|
||||
}
|
||||
|
||||
@@ -215,10 +215,11 @@ class CommandOSSet : public Command
|
||||
"users will not be allowed to modify any Services data,\n"
|
||||
"including channel and nickname access lists, etc. IRCops\n"
|
||||
"with sufficient Services privileges will be able to modify\n"
|
||||
"Services' AKILL list and drop or forbid nicknames and\n"
|
||||
"channels, but any such changes will not be saved unless\n"
|
||||
"read-only mode is deactivated before Services is terminated\n"
|
||||
"or restarted.\n"
|
||||
"Services' AKILL, SQLINE, SNLINE and ignore lists, drop,\n"
|
||||
"suspend or forbid nicknames and channels, and manage news,\n"
|
||||
"oper info and DNS, but any such changes will not be saved\n"
|
||||
"unless read-only mode is deactivated before Services are\n"
|
||||
"terminated or restarted.\n"
|
||||
" \n"
|
||||
"This option is equivalent to the command-line option\n"
|
||||
"\002--readonly\002."));
|
||||
|
||||
@@ -95,6 +95,7 @@ class CommandOSSXLineBase : public Command
|
||||
|
||||
SXLineDelCallback::DoDel(this->xlm(), source, x);
|
||||
source.Reply(_("\002%s\002 deleted from the %s list."), mask.c_str(), source.command.c_str());
|
||||
Log(LOG_ADMIN, source, this) << "to remove " << mask << " from the list";
|
||||
}
|
||||
|
||||
if (Anope::ReadOnly)
|
||||
@@ -207,7 +208,10 @@ class CommandOSSXLineBase : public Command
|
||||
this->xlm()->DelXLine(x);
|
||||
}
|
||||
|
||||
Log(LOG_ADMIN, source, this) << "to CLEAR the list";
|
||||
source.Reply(_("The %s list has been cleared."), source.command.c_str());
|
||||
if (Anope::ReadOnly)
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -400,7 +404,7 @@ class CommandOSSNLine : public CommandOSSXLineBase
|
||||
}
|
||||
|
||||
source.Reply(_("\002%s\002 added to the %s list."), mask.c_str(), source.command.c_str());
|
||||
Log(LOG_ADMIN, source, this) << "on " << mask << " (" << reason << ") expires in " << (expires ? Anope::Duration(expires - Anope::CurTime) : "never") << " [affects " << affected << " user(s) (" << percent << "%)]";
|
||||
Log(LOG_ADMIN, source, this) << "on " << mask << " (" << reason << "), expires in " << (expires ? Anope::Duration(expires - Anope::CurTime) : "never") << " [affects " << affected << " user(s) (" << percent << "%)]";
|
||||
if (Anope::ReadOnly)
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
}
|
||||
@@ -428,15 +432,16 @@ class CommandOSSNLine : public CommandOSSXLineBase
|
||||
"\002SNLINE ADD\002 adds the given realname mask to the SNLINE\n"
|
||||
"list for the given reason (which \002must\002 be given).\n"
|
||||
"\037expiry\037 is specified as an integer followed by one of \037d\037\n"
|
||||
"(days), \037h\037 (hours), or \037m\037 (minutes). Combinations (such as\n"
|
||||
"\0371h30m\037) are not permitted. If a unit specifier is not\n"
|
||||
"(days), \037h\037 (hours), or \037m\037 (minutes). Combinations (such as\n"
|
||||
"\0371h30m\037) are not permitted. If a unit specifier is not\n"
|
||||
"included, the default is days (so \037+30\037 by itself means 30\n"
|
||||
"days). To add an SNLINE which does not expire, use \037+0\037. If the\n"
|
||||
"days). To add an SNLINE which does not expire, use \037+0\037. If the\n"
|
||||
"realname mask to be added starts with a \037+\037, an expiry time must\n"
|
||||
"be given, even if it is the same as the default. The\n"
|
||||
"current SNLINE default expiry time can be found with the\n"
|
||||
"\002STATS AKILL\002 command.\n"
|
||||
"Note: because the realname mask may contain spaces, the\n"
|
||||
" \n"
|
||||
"\002Note\002: because the realname mask may contain spaces, the\n"
|
||||
"separator between it and the reason is a colon."));
|
||||
const Anope::string ®exengine = Config->GetBlock("options")->Get<const Anope::string>("regexengine");
|
||||
if (!regexengine.empty())
|
||||
@@ -631,9 +636,8 @@ class CommandOSSQLine : public CommandOSSXLineBase
|
||||
this->xlm()->Send(NULL, x);
|
||||
}
|
||||
|
||||
source.Reply(_("\002%s\002 added to the SQLINE list."), mask.c_str());
|
||||
Log(LOG_ADMIN, source, this) << "on " << mask << " (" << reason << ") expires in " << (expires ? Anope::Duration(expires - Anope::CurTime) : "never") << " [affects " << affected << " user(s) (" << percent << "%)]";
|
||||
|
||||
source.Reply(_("\002%s\002 added to the %s list."), mask.c_str(), source.command.c_str());
|
||||
Log(LOG_ADMIN, source, this) << "on " << mask << " (" << reason << "), expires in " << (expires ? Anope::Duration(expires - Anope::CurTime) : "never") << " [affects " << affected << " user(s) (" << percent << "%)]";
|
||||
if (Anope::ReadOnly)
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user