OperServ: add FORBID (nick/channel/email registration bans)
All checks were successful
CI / check (push) Successful in 3m56s
All checks were successful
CI / check (push) Successful in 3m56s
FORBID ADD/DEL/LIST bans a NICK, CHAN, or EMAIL glob from being registered — a network-wide policy that gossips like an AKILL and every node enforces. Nick registration is blocked in pre_register_check (both NickServ REGISTER and the account-registration relay), and channel registration in ChanServ REGISTER. New Forbid store + events, mirroring the akill subsystem.
This commit is contained in:
parent
ac50af92fc
commit
be4860c9a8
12 changed files with 220 additions and 1 deletions
|
|
@ -124,6 +124,10 @@ impl Service for ChanServ {
|
|||
ctx.notice(me, from.uid, format!("You must be a channel operator (\x02@\x02) in \x02{chan}\x02 to register it."));
|
||||
return;
|
||||
}
|
||||
if let Some(reason) = db.is_forbidden("CHAN", chan) {
|
||||
ctx.notice(me, from.uid, format!("\x02{chan}\x02 can't be registered: {reason}"));
|
||||
return;
|
||||
}
|
||||
match db.register_channel(chan, account) {
|
||||
Ok(()) => {
|
||||
ctx.channel_mode(me, chan, "+r"); // mark the channel registered
|
||||
|
|
|
|||
62
modules/operserv/src/forbid.rs
Normal file
62
modules/operserv/src/forbid.rs
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
use echo_api::{human_time, Priv, Sender, ServiceCtx, Store};
|
||||
|
||||
// FORBID ADD <NICK|CHAN|EMAIL> <mask> <reason> | DEL <NICK|CHAN|EMAIL> <mask> | LIST
|
||||
// Bans a nick, channel, or email pattern from being registered. Admin-only.
|
||||
pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db: &mut dyn Store) {
|
||||
if !from.privs.has(Priv::Admin) {
|
||||
ctx.notice(me, from.uid, "Access denied — FORBID needs the \x02admin\x02 privilege.");
|
||||
return;
|
||||
}
|
||||
match args.get(1).map(|s| s.to_ascii_uppercase()).as_deref() {
|
||||
Some("ADD") => {
|
||||
let (Some(kind), Some(&mask)) = (args.get(2).and_then(|k| norm_kind(k)), args.get(3)) else {
|
||||
ctx.notice(me, from.uid, "Syntax: FORBID ADD <NICK|CHAN|EMAIL> <mask> <reason>");
|
||||
return;
|
||||
};
|
||||
if args.len() < 5 {
|
||||
ctx.notice(me, from.uid, "Please give a reason.");
|
||||
return;
|
||||
}
|
||||
let reason = args[4..].join(" ");
|
||||
let setter = from.account.unwrap_or(from.nick);
|
||||
match db.forbid_add(kind, mask, setter, &reason) {
|
||||
Ok(true) => ctx.notice(me, from.uid, format!("Forbade {} \x02{mask}\x02.", kind.to_lowercase())),
|
||||
Ok(false) => ctx.notice(me, from.uid, format!("Updated the forbid on {} \x02{mask}\x02.", kind.to_lowercase())),
|
||||
Err(_) => ctx.notice(me, from.uid, "Sorry, that didn't work. Please try again in a moment."),
|
||||
}
|
||||
}
|
||||
Some("DEL") | Some("REMOVE") => {
|
||||
let (Some(kind), Some(&mask)) = (args.get(2).and_then(|k| norm_kind(k)), args.get(3)) else {
|
||||
ctx.notice(me, from.uid, "Syntax: FORBID DEL <NICK|CHAN|EMAIL> <mask>");
|
||||
return;
|
||||
};
|
||||
match db.forbid_del(kind, mask) {
|
||||
Ok(true) => ctx.notice(me, from.uid, format!("Removed the forbid on {} \x02{mask}\x02.", kind.to_lowercase())),
|
||||
Ok(false) => ctx.notice(me, from.uid, format!("No forbid on {} \x02{mask}\x02.", kind.to_lowercase())),
|
||||
Err(_) => ctx.notice(me, from.uid, "Sorry, that didn't work. Please try again in a moment."),
|
||||
}
|
||||
}
|
||||
Some("LIST") | Some("VIEW") => {
|
||||
let forbids = db.forbids();
|
||||
if forbids.is_empty() {
|
||||
ctx.notice(me, from.uid, "The forbid list is empty.");
|
||||
return;
|
||||
}
|
||||
ctx.notice(me, from.uid, "Registration bans:");
|
||||
for f in &forbids {
|
||||
ctx.notice(me, from.uid, format!(" [{}] \x02{}\x02 by {} ({}) — {}", f.kind, f.mask, f.setter, human_time(f.ts), f.reason));
|
||||
}
|
||||
}
|
||||
_ => ctx.notice(me, from.uid, "Syntax: FORBID ADD <NICK|CHAN|EMAIL> <mask> <reason> | DEL <NICK|CHAN|EMAIL> <mask> | LIST"),
|
||||
}
|
||||
}
|
||||
|
||||
// Canonicalise the kind argument to NICK / CHAN / EMAIL.
|
||||
fn norm_kind(k: &str) -> Option<&'static str> {
|
||||
match k.to_ascii_uppercase().as_str() {
|
||||
"NICK" | "NICKNAME" | "ACCOUNT" => Some("NICK"),
|
||||
"CHAN" | "CHANNEL" => Some("CHAN"),
|
||||
"EMAIL" | "MAIL" => Some("EMAIL"),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,8 @@ use echo_api::{HelpEntry, NetView, Sender, Service, ServiceCtx, Store};
|
|||
|
||||
#[path = "xline.rs"]
|
||||
mod xline;
|
||||
#[path = "forbid.rs"]
|
||||
mod forbid;
|
||||
#[path = "global.rs"]
|
||||
mod global;
|
||||
#[path = "kill.rs"]
|
||||
|
|
@ -68,6 +70,7 @@ impl Service for OperServ {
|
|||
Some(cmd) if cmd.eq_ignore_ascii_case("SNLINE") => xline::SNLINE.handle(me, from, args, ctx, db),
|
||||
Some(cmd) if cmd.eq_ignore_ascii_case("SHUN") => xline::SHUN.handle(me, from, args, ctx, db),
|
||||
Some(cmd) if cmd.eq_ignore_ascii_case("CBAN") => xline::CBAN.handle(me, from, args, ctx, db),
|
||||
Some(cmd) if cmd.eq_ignore_ascii_case("FORBID") => forbid::handle(me, from, args, ctx, db),
|
||||
Some(cmd) if cmd.eq_ignore_ascii_case("GLOBAL") => global::handle(me, from, args, ctx),
|
||||
Some(cmd) if cmd.eq_ignore_ascii_case("KILL") => kill::handle(me, from, args, ctx, net),
|
||||
Some(cmd) if cmd.eq_ignore_ascii_case("KICK") => kick::handle(me, from, args, ctx, net),
|
||||
|
|
@ -95,6 +98,7 @@ const BLURB: &str = "OperServ holds the network operator tools. Every command is
|
|||
|
||||
const TOPICS: &[HelpEntry] = &[
|
||||
HelpEntry { cmd: "AKILL", summary: "network-ban a user@host", detail: "Syntax: \x02AKILL ADD [+expiry] <user@host> <reason> | AKILL DEL <mask|number> | AKILL LIST [pattern]\x02\nA network-wide user@host ban." },
|
||||
HelpEntry { cmd: "FORBID", summary: "ban a nick/chan/email from registration", detail: "Syntax: \x02FORBID ADD <NICK|CHAN|EMAIL> <mask> <reason> | FORBID DEL <NICK|CHAN|EMAIL> <mask> | FORBID LIST\x02\nStops a nick, channel, or email pattern from being registered." },
|
||||
HelpEntry { cmd: "SQLINE", summary: "ban a nick pattern", detail: "Syntax: \x02SQLINE ADD [+expiry] <nick> <reason> | SQLINE DEL <mask|number> | SQLINE LIST [pattern]\x02\nBans matching nicknames." },
|
||||
HelpEntry { cmd: "SNLINE", summary: "ban a realname pattern", detail: "Syntax: \x02SNLINE ADD [+expiry] <realname> <reason> | SNLINE DEL <mask|number> | SNLINE LIST [pattern]\x02\nBans matching real names." },
|
||||
HelpEntry { cmd: "SHUN", summary: "silence a host", detail: "Syntax: \x02SHUN ADD [+expiry] <mask> <reason> | SHUN DEL <mask|number> | SHUN LIST [pattern]\x02\nSilences a matching host: it stays connected but can't act." },
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue