botserv: bots join channels as protected admins (+a) so ordinary ops can't kick or deop them
All checks were successful
CI / check (push) Successful in 3m50s
All checks were successful
CI / check (push) Successful in 3m50s
This commit is contained in:
parent
e2330c56ff
commit
73467b91ca
4 changed files with 25 additions and 21 deletions
|
|
@ -117,8 +117,10 @@ pub enum NetAction {
|
||||||
ForcePart { uid: String, channel: String, reason: String },
|
ForcePart { uid: String, channel: String, reason: String },
|
||||||
// Remove one of our pseudo-clients (e.g. a deleted bot) from the network.
|
// Remove one of our pseudo-clients (e.g. a deleted bot) from the network.
|
||||||
QuitUser { uid: String, reason: String },
|
QuitUser { uid: String, reason: String },
|
||||||
// A services pseudo-client (a bot) joins / parts a channel.
|
// A services pseudo-client (a bot) joins / parts a channel. `modes` are the
|
||||||
ServiceJoin { uid: String, channel: String },
|
// status-mode letters it joins with: "a" (protected admin) for BotServ bots,
|
||||||
|
// "o" for the core service pseudo-clients in the services channel.
|
||||||
|
ServiceJoin { uid: String, channel: String, modes: String },
|
||||||
ServicePart { uid: String, channel: String },
|
ServicePart { uid: String, channel: String },
|
||||||
// Set channel modes from services, e.g. +r on a registered channel. `from` is
|
// Set channel modes from services, e.g. +r on a registered channel. `from` is
|
||||||
// the pseudoclient uid to source it from (empty = the services server). The
|
// the pseudoclient uid to source it from (empty = the services server). The
|
||||||
|
|
|
||||||
|
|
@ -350,14 +350,14 @@ impl Protocol for InspIrcd {
|
||||||
// SVSNICK <uid> <newnick> <nickts> — the new nick takes the current
|
// SVSNICK <uid> <newnick> <nickts> — the new nick takes the current
|
||||||
// time as its TS so it wins any collision resolution.
|
// time as its TS so it wins any collision resolution.
|
||||||
NetAction::QuitUser { uid, reason } => vec![format!(":{} QUIT :{}", uid, reason)],
|
NetAction::QuitUser { uid, reason } => vec![format!(":{} QUIT :{}", uid, reason)],
|
||||||
NetAction::ServiceJoin { uid, channel } => {
|
NetAction::ServiceJoin { uid, channel, modes } => {
|
||||||
// IJOIN needs a membid (`IJOIN <chan> <membid>`); without it the
|
// IJOIN needs a membid (`IJOIN <chan> <membid>`); without it the
|
||||||
// ircd rejects the whole link with "Insufficient parameters". The
|
// ircd rejects the whole link with "Insufficient parameters". The
|
||||||
// trailing "1 o" is the 4-param form (ts, status modes): the bot
|
// trailing "1 <modes>" is the 4-param form (ts, status modes) — a
|
||||||
// joins opped, matching Anope's default bot modes (+o). ts 1 keeps
|
// BotServ bot joins "+a" (protected admin), core services "+o". ts 1
|
||||||
// it below the channel TS so the op is always applied.
|
// keeps it below the channel TS so the status is always applied.
|
||||||
self.membid += 1;
|
self.membid += 1;
|
||||||
vec![format!(":{} IJOIN {} {} 1 o", uid, channel, self.membid)]
|
vec![format!(":{} IJOIN {} {} 1 {}", uid, channel, self.membid, modes)]
|
||||||
}
|
}
|
||||||
NetAction::ServicePart { uid, channel } => vec![format!(":{} PART {}", uid, channel)],
|
NetAction::ServicePart { uid, channel } => vec![format!(":{} PART {}", uid, channel)],
|
||||||
// ENCAP the target's server: CHGHOST <uid> <newhost>, to set a vhost.
|
// ENCAP the target's server: CHGHOST <uid> <newhost>, to set a vhost.
|
||||||
|
|
@ -745,10 +745,10 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn service_join_ijoin_includes_membid() {
|
fn service_join_ijoin_includes_membid() {
|
||||||
let mut p = proto();
|
let mut p = proto();
|
||||||
let a = p.serialize(&NetAction::ServiceJoin { uid: "00DB00000".into(), channel: "#taverne".into() });
|
let a = p.serialize(&NetAction::ServiceJoin { uid: "00DB00000".into(), channel: "#taverne".into(), modes: "a".into() });
|
||||||
assert_eq!(a, vec![":00DB00000 IJOIN #taverne 1 1 o".to_string()], "joins opped with a membid");
|
assert_eq!(a, vec![":00DB00000 IJOIN #taverne 1 1 a".to_string()], "bot joins +a (protected) with a membid");
|
||||||
// membid is monotonic across joins
|
// membid is monotonic across joins; core services join +o
|
||||||
let b = p.serialize(&NetAction::ServiceJoin { uid: "00DB00000".into(), channel: "#quizz".into() });
|
let b = p.serialize(&NetAction::ServiceJoin { uid: "00DB00000".into(), channel: "#quizz".into(), modes: "o".into() });
|
||||||
assert_eq!(b, vec![":00DB00000 IJOIN #quizz 2 1 o".to_string()]);
|
assert_eq!(b, vec![":00DB00000 IJOIN #quizz 2 1 o".to_string()]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -444,7 +444,9 @@ impl Engine {
|
||||||
for (bot_lc, chan) in &desired {
|
for (bot_lc, chan) in &desired {
|
||||||
if !self.bot_channels.contains(&(bot_lc.clone(), chan.clone())) {
|
if !self.bot_channels.contains(&(bot_lc.clone(), chan.clone())) {
|
||||||
if let Some(uid) = self.bot_uids.get(bot_lc) {
|
if let Some(uid) = self.bot_uids.get(bot_lc) {
|
||||||
out.push(NetAction::ServiceJoin { uid: uid.clone(), channel: chan.clone() });
|
// BotServ bots join as protected admins (+a) so ordinary ops
|
||||||
|
// can't kick or deop them — the standard bot default.
|
||||||
|
out.push(NetAction::ServiceJoin { uid: uid.clone(), channel: chan.clone(), modes: "a".into() });
|
||||||
self.bot_channels.insert((bot_lc.clone(), chan.clone()));
|
self.bot_channels.insert((bot_lc.clone(), chan.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1025,7 +1027,7 @@ impl Engine {
|
||||||
}
|
}
|
||||||
// All service pseudo-clients sit in the services channel (configurable).
|
// All service pseudo-clients sit in the services channel (configurable).
|
||||||
if !self.services_channel.is_empty() {
|
if !self.services_channel.is_empty() {
|
||||||
out.push(NetAction::ServiceJoin { uid: svc.uid().to_string(), channel: self.services_channel.clone() });
|
out.push(NetAction::ServiceJoin { uid: svc.uid().to_string(), channel: self.services_channel.clone(), modes: "o".into() });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Advertise our SASL mechanisms so the uplink can offer `sasl=PLAIN` to
|
// Advertise our SASL mechanisms so the uplink can offer `sasl=PLAIN` to
|
||||||
|
|
@ -1036,7 +1038,7 @@ impl Engine {
|
||||||
// already joined it above as the services channel.
|
// already joined it above as the services channel.
|
||||||
if let (false, Some(chan)) = (self.debugserv_uid.is_empty(), self.log_channel.clone()) {
|
if let (false, Some(chan)) = (self.debugserv_uid.is_empty(), self.log_channel.clone()) {
|
||||||
if !chan.eq_ignore_ascii_case(&self.services_channel) {
|
if !chan.eq_ignore_ascii_case(&self.services_channel) {
|
||||||
out.push(NetAction::ServiceJoin { uid: self.debugserv_uid.clone(), channel: chan });
|
out.push(NetAction::ServiceJoin { uid: self.debugserv_uid.clone(), channel: chan, modes: "o".into() });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Re-assert the network bans over the fresh link, each with its remaining
|
// Re-assert the network bans over the fresh link, each with its remaining
|
||||||
|
|
|
||||||
|
|
@ -546,7 +546,7 @@
|
||||||
e.handle(NetEvent::Privmsg { from: "000AAAAAB".into(), to: "42SAAAAAD".into(), text: "BOT ADD Bendy bot serv.host Helper".into() });
|
e.handle(NetEvent::Privmsg { from: "000AAAAAB".into(), to: "42SAAAAAD".into(), text: "BOT ADD Bendy bot serv.host Helper".into() });
|
||||||
let out = e.handle(NetEvent::Privmsg { from: "000AAAAAB".into(), to: "42SAAAAAD".into(), text: "ASSIGN #room Bendy".into() });
|
let out = e.handle(NetEvent::Privmsg { from: "000AAAAAB".into(), to: "42SAAAAAD".into(), text: "ASSIGN #room Bendy".into() });
|
||||||
let botuid = out.iter().find_map(|a| match a {
|
let botuid = out.iter().find_map(|a| match a {
|
||||||
NetAction::ServiceJoin { uid, channel } if channel == "#room" => Some(uid.clone()),
|
NetAction::ServiceJoin { uid, channel, .. } if channel == "#room" => Some(uid.clone()),
|
||||||
_ => None,
|
_ => None,
|
||||||
}).expect("bot joins #room");
|
}).expect("bot joins #room");
|
||||||
|
|
||||||
|
|
@ -591,7 +591,7 @@
|
||||||
let _ = assign;
|
let _ = assign;
|
||||||
let out = e.handle(NetEvent::Privmsg { from: "000AAAAAA".into(), to: "42SAAAAAD".into(), text: "ASSIGN #room Bendy".into() });
|
let out = e.handle(NetEvent::Privmsg { from: "000AAAAAA".into(), to: "42SAAAAAD".into(), text: "ASSIGN #room Bendy".into() });
|
||||||
let botuid = out.iter().find_map(|a| match a {
|
let botuid = out.iter().find_map(|a| match a {
|
||||||
NetAction::ServiceJoin { uid, channel } if channel == "#room" => Some(uid.clone()),
|
NetAction::ServiceJoin { uid, channel, .. } if channel == "#room" => Some(uid.clone()),
|
||||||
_ => None,
|
_ => None,
|
||||||
}).expect("bot joins #room");
|
}).expect("bot joins #room");
|
||||||
|
|
||||||
|
|
@ -2217,9 +2217,9 @@
|
||||||
ns(&mut e, "000AAAAAC", "IDENTIFY password1");
|
ns(&mut e, "000AAAAAC", "IDENTIFY password1");
|
||||||
bs(&mut e, "000AAAAAC", "BOT ADD Bendy bot serv.host Helper"); // goes live
|
bs(&mut e, "000AAAAAC", "BOT ADD Bendy bot serv.host Helper"); // goes live
|
||||||
|
|
||||||
// The founder assigns the bot: it joins the channel.
|
// The founder assigns the bot: it joins the channel as a protected admin (+a).
|
||||||
let out = bs(&mut e, "000AAAAAB", "ASSIGN #c Bendy");
|
let out = bs(&mut e, "000AAAAAB", "ASSIGN #c Bendy");
|
||||||
assert!(out.iter().any(|a| matches!(a, NetAction::ServiceJoin { channel, .. } if channel == "#c")), "joins on assign: {out:?}");
|
assert!(out.iter().any(|a| matches!(a, NetAction::ServiceJoin { channel, modes, .. } if channel == "#c" && modes == "a")), "joins +a on assign: {out:?}");
|
||||||
// Unassigning parts it.
|
// Unassigning parts it.
|
||||||
let out = bs(&mut e, "000AAAAAB", "UNASSIGN #c");
|
let out = bs(&mut e, "000AAAAAB", "UNASSIGN #c");
|
||||||
assert!(out.iter().any(|a| matches!(a, NetAction::ServicePart { channel, .. } if channel == "#c")), "parts on unassign: {out:?}");
|
assert!(out.iter().any(|a| matches!(a, NetAction::ServicePart { channel, .. } if channel == "#c")), "parts on unassign: {out:?}");
|
||||||
|
|
@ -2254,13 +2254,13 @@
|
||||||
ns(&mut e, "IDENTIFY password1");
|
ns(&mut e, "IDENTIFY password1");
|
||||||
bs(&mut e, "BOT ADD Bendy bot serv.host Helper");
|
bs(&mut e, "BOT ADD Bendy bot serv.host Helper");
|
||||||
let bot_uid = bs(&mut e, "ASSIGN #c Bendy").iter().find_map(|a| match a {
|
let bot_uid = bs(&mut e, "ASSIGN #c Bendy").iter().find_map(|a| match a {
|
||||||
NetAction::ServiceJoin { uid, channel } if channel == "#c" => Some(uid.clone()),
|
NetAction::ServiceJoin { uid, channel, .. } if channel == "#c" => Some(uid.clone()),
|
||||||
_ => None,
|
_ => None,
|
||||||
}).expect("bot joined on assign");
|
}).expect("bot joined on assign");
|
||||||
|
|
||||||
// The bot is kicked out (the ircd relays this as a PART for its uid).
|
// The bot is kicked out (the ircd relays this as a PART for its uid).
|
||||||
let out = e.handle(NetEvent::Part { uid: bot_uid.clone(), channel: "#c".into() });
|
let out = e.handle(NetEvent::Part { uid: bot_uid.clone(), channel: "#c".into() });
|
||||||
assert!(out.iter().any(|a| matches!(a, NetAction::ServiceJoin { uid, channel } if uid == &bot_uid && channel == "#c")), "kicked bot rejoins: {out:?}");
|
assert!(out.iter().any(|a| matches!(a, NetAction::ServiceJoin { uid, channel, .. } if uid == &bot_uid && channel == "#c")), "kicked bot rejoins: {out:?}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// A KILL forgets a real user, but a killed services bot is reintroduced and
|
// A KILL forgets a real user, but a killed services bot is reintroduced and
|
||||||
|
|
@ -2292,7 +2292,7 @@
|
||||||
ns(&mut e, "IDENTIFY password1");
|
ns(&mut e, "IDENTIFY password1");
|
||||||
bs(&mut e, "BOT ADD Bendy bot serv.host Helper");
|
bs(&mut e, "BOT ADD Bendy bot serv.host Helper");
|
||||||
let bot_uid = bs(&mut e, "ASSIGN #c Bendy").iter().find_map(|a| match a {
|
let bot_uid = bs(&mut e, "ASSIGN #c Bendy").iter().find_map(|a| match a {
|
||||||
NetAction::ServiceJoin { uid, channel } if channel == "#c" => Some(uid.clone()),
|
NetAction::ServiceJoin { uid, channel, .. } if channel == "#c" => Some(uid.clone()),
|
||||||
_ => None,
|
_ => None,
|
||||||
}).expect("bot joined on assign");
|
}).expect("bot joined on assign");
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue