OperServ: add FORBID (nick/channel/email registration bans)
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:
Jean Chevronnet 2026-07-15 19:14:07 +00:00
parent ac50af92fc
commit be4860c9a8
No known key found for this signature in database
12 changed files with 220 additions and 1 deletions

View file

@ -118,6 +118,10 @@ pub enum Event {
kind: String,
mask: String,
},
// Registration bans (OperServ FORBID). Global network policy. `kind` is
// "NICK", "CHAN", or "EMAIL".
ForbidAdded { kind: String, mask: String, setter: String, reason: String, ts: u64 },
ForbidRemoved { kind: String, mask: String },
}
// Whether an event replicates across the federation. Account identity is Global
@ -160,6 +164,8 @@ impl Event {
| Event::AccountOperNoteSet { .. }
| Event::AkillAdded { .. }
| Event::AkillRemoved { .. }
| Event::ForbidAdded { .. }
| Event::ForbidRemoved { .. }
| Event::NewsAdded { .. }
| Event::NewsDeleted { .. }
| Event::ReportFiled { .. }
@ -495,6 +501,13 @@ pub(crate) fn apply(accounts: &mut HashMap<String, Account>, channels: &mut Hash
Event::AkillRemoved { kind, mask } => {
net.akills.retain(|a| !(a.kind == kind && a.mask.eq_ignore_ascii_case(&mask)));
}
Event::ForbidAdded { kind, mask, setter, reason, ts } => {
net.forbids.retain(|f| !(f.kind == kind && f.mask.eq_ignore_ascii_case(&mask)));
net.forbids.push(Forbid { kind, mask, setter, reason, ts });
}
Event::ForbidRemoved { kind, mask } => {
net.forbids.retain(|f| !(f.kind == kind && f.mask.eq_ignore_ascii_case(&mask)));
}
Event::AccountExpiryWarned { account } => {
if let Some(a) = accounts.get_mut(&key(&account)) {
a.expiry_warned = true;