OperServ: JUPE — hold a server name against a rogue link
JUPE <server.name> [reason] introduces a fake server holding the name so the real one can't link; JUPE DEL <name> squits it; JUPE LIST shows them. Node-local (the introducing node owns the jupe, so it isn't federated — that would collide SIDs across nodes) and re-asserted at burst. A fake server id is allocated per jupe. New JupeServer/Squit actions serialise to the mid-link SERVER introduction and SQUIT. Admin-only.
This commit is contained in:
parent
76a7162227
commit
1daebc94a5
6 changed files with 179 additions and 2 deletions
49
operserv/src/jupe.rs
Normal file
49
operserv/src/jupe.rs
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
use fedserv_api::{Priv, Sender, ServiceCtx, Store};
|
||||
|
||||
// JUPE <server.name> [reason] | JUPE DEL <server.name> | JUPE LIST: hold a
|
||||
// server name with a fake server so a rogue one can't link (or lift it). Admin-
|
||||
// only. Node-local: the introducing node owns the jupe.
|
||||
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 — JUPE needs the \x02admin\x02 privilege.");
|
||||
return;
|
||||
}
|
||||
match args.get(1) {
|
||||
Some(&sub) if sub.eq_ignore_ascii_case("DEL") || sub.eq_ignore_ascii_case("REMOVE") => del(me, from, args.get(2).copied(), ctx, db),
|
||||
Some(&sub) if sub.eq_ignore_ascii_case("LIST") => list(me, from, ctx, db),
|
||||
Some(&name) if name.contains('.') => {
|
||||
let reason = if args.len() > 2 { args[2..].join(" ") } else { "Juped by services".to_string() };
|
||||
let by = from.account.unwrap_or(from.nick);
|
||||
let sid = db.jupe_add(name, &format!("({by}) {reason}"));
|
||||
ctx.jupe(name, &sid, &format!("({by}) {reason}"));
|
||||
ctx.notice(me, from.uid, format!("\x02{name}\x02 is now juped."));
|
||||
}
|
||||
_ => ctx.notice(me, from.uid, "Syntax: JUPE <server.name> [reason] | JUPE DEL <server.name> | JUPE LIST"),
|
||||
}
|
||||
}
|
||||
|
||||
fn del(me: &str, from: &Sender, name: Option<&str>, ctx: &mut ServiceCtx, db: &mut dyn Store) {
|
||||
let Some(name) = name else {
|
||||
ctx.notice(me, from.uid, "Syntax: JUPE DEL <server.name>");
|
||||
return;
|
||||
};
|
||||
match db.jupe_del(name) {
|
||||
Some(sid) => {
|
||||
ctx.squit(&sid, "Jupe lifted");
|
||||
ctx.notice(me, from.uid, format!("The jupe on \x02{name}\x02 has been lifted."));
|
||||
}
|
||||
None => ctx.notice(me, from.uid, format!("\x02{name}\x02 isn't juped.")),
|
||||
}
|
||||
}
|
||||
|
||||
fn list(me: &str, from: &Sender, ctx: &mut ServiceCtx, db: &mut dyn Store) {
|
||||
let jupes = db.jupes();
|
||||
if jupes.is_empty() {
|
||||
ctx.notice(me, from.uid, "No servers are juped.");
|
||||
return;
|
||||
}
|
||||
for (name, sid, reason) in &jupes {
|
||||
ctx.notice(me, from.uid, format!(" \x02{name}\x02 ({sid}) — {reason}"));
|
||||
}
|
||||
ctx.notice(me, from.uid, format!("End of jupe list ({} shown).", jupes.len()));
|
||||
}
|
||||
|
|
@ -29,6 +29,8 @@ mod news;
|
|||
mod oper;
|
||||
#[path = "session.rs"]
|
||||
mod session;
|
||||
#[path = "jupe.rs"]
|
||||
mod jupe;
|
||||
|
||||
pub struct OperServ {
|
||||
pub uid: String,
|
||||
|
|
@ -69,6 +71,7 @@ impl Service for OperServ {
|
|||
Some(cmd) if cmd.eq_ignore_ascii_case("OPER") => oper::handle(me, from, args, ctx, db),
|
||||
Some(cmd) if cmd.eq_ignore_ascii_case("SESSION") => session::handle_session(me, from, args, ctx, net),
|
||||
Some(cmd) if cmd.eq_ignore_ascii_case("EXCEPTION") => session::handle_exception(me, from, args, ctx, db),
|
||||
Some(cmd) if cmd.eq_ignore_ascii_case("JUPE") => jupe::handle(me, from, args, ctx, db),
|
||||
Some(cmd) if cmd.eq_ignore_ascii_case("HELP") => help(me, from, ctx),
|
||||
None => help(me, from, ctx),
|
||||
Some(other) => ctx.notice(me, from.uid, format!("I don't know \x02{other}\x02. Try \x02HELP\x02.")),
|
||||
|
|
@ -77,5 +80,5 @@ impl Service for OperServ {
|
|||
}
|
||||
|
||||
fn help(me: &str, from: &Sender, ctx: &mut ServiceCtx) {
|
||||
ctx.notice(me, from.uid, "OperServ holds network operator tools (Priv::Admin): \x02AKILL\x02 ADD|DEL|LIST (user@host bans), \x02SQLINE\x02 ADD|DEL|LIST (nick bans), \x02SNLINE\x02 ADD|DEL|LIST (realname bans), \x02GLOBAL\x02 <message> (announce to everyone), \x02KILL\x02 <nick> [reason] (disconnect a user), \x02KICK\x02 <#chan> <nick> [reason], \x02MODE\x02 <#chan> <modes> [params], \x02IGNORE\x02 ADD|DEL|LIST (silence a user from services), \x02STATS\x02 (enforcement overview), \x02SVSNICK\x02 <nick> <newnick>, \x02SVSJOIN\x02 <nick> <#chan> [key], \x02INFO\x02 ADD|DEL <target> (staff notes), \x02NEWS\x02 ADD|DEL|LIST <LOGON|OPER> (announcements), \x02OPER\x02 ADD|DEL|LIST (runtime operators), \x02SESSION\x02 LIST|VIEW + \x02EXCEPTION\x02 ADD|DEL|LIST (per-IP session limits).");
|
||||
ctx.notice(me, from.uid, "OperServ holds network operator tools (Priv::Admin): \x02AKILL\x02 ADD|DEL|LIST (user@host bans), \x02SQLINE\x02 ADD|DEL|LIST (nick bans), \x02SNLINE\x02 ADD|DEL|LIST (realname bans), \x02GLOBAL\x02 <message> (announce to everyone), \x02KILL\x02 <nick> [reason] (disconnect a user), \x02KICK\x02 <#chan> <nick> [reason], \x02MODE\x02 <#chan> <modes> [params], \x02IGNORE\x02 ADD|DEL|LIST (silence a user from services), \x02STATS\x02 (enforcement overview), \x02SVSNICK\x02 <nick> <newnick>, \x02SVSJOIN\x02 <nick> <#chan> [key], \x02INFO\x02 ADD|DEL <target> (staff notes), \x02NEWS\x02 ADD|DEL|LIST <LOGON|OPER> (announcements), \x02OPER\x02 ADD|DEL|LIST (runtime operators), \x02SESSION\x02 LIST|VIEW + \x02EXCEPTION\x02 ADD|DEL|LIST (per-IP session limits), \x02JUPE\x02 <server> | DEL | LIST.");
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue