db: persist jupes across restart (event-source them)
All checks were successful
CI / check (push) Successful in 3m50s
All checks were successful
CI / check (push) Successful in 3m50s
A fold-parity property test caught it: jupes were plain in-memory Db fields that logged no event, so after any services restart every juped server was silently un-juped and could relink. Moved jupes into NetData (like akills/ forbids/groups) and event-sourced them with JupeAdded/JupeRemoved (Local scope), so a jupe now replays from the log. Adds the round-trip property test (live state == replayed state) as a permanent guard against this bug class.
This commit is contained in:
parent
61005f52f5
commit
90167dd46f
6 changed files with 119 additions and 13 deletions
|
|
@ -132,6 +132,10 @@ pub enum Event {
|
|||
// "NICK", "CHAN", or "EMAIL".
|
||||
ForbidAdded { kind: String, mask: String, setter: String, reason: String, ts: u64 },
|
||||
ForbidRemoved { kind: String, mask: String },
|
||||
// Server jupes (OperServ JUPE). Node-local (each network holds a rogue server
|
||||
// by minting a fake one on `sid`), so Local scope but still persisted.
|
||||
JupeAdded { name: String, sid: String, reason: String },
|
||||
JupeRemoved { name: String },
|
||||
}
|
||||
|
||||
// Whether an event replicates across the federation. Account identity is Global
|
||||
|
|
@ -232,7 +236,9 @@ impl Event {
|
|||
| Event::ChannelUsed { .. }
|
||||
| Event::ChannelNoExpire { .. }
|
||||
| Event::ChannelExpiryWarned { .. }
|
||||
| Event::ChannelOperNoteSet { .. } => Scope::Local,
|
||||
| Event::ChannelOperNoteSet { .. }
|
||||
| Event::JupeAdded { .. }
|
||||
| Event::JupeRemoved { .. } => Scope::Local,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -603,6 +609,17 @@ pub(crate) fn apply(accounts: &mut HashMap<String, Account>, channels: &mut Hash
|
|||
Event::ForbidRemoved { kind, mask } => {
|
||||
net.forbids.retain(|f| !(f.kind == kind && f.mask.eq_ignore_ascii_case(&mask)));
|
||||
}
|
||||
Event::JupeAdded { name, sid, reason } => {
|
||||
// Idempotent over a snapshot; keep jupe_seq ahead so a fresh add can't
|
||||
// reuse a sid.
|
||||
if !net.jupes.iter().any(|j| j.name.eq_ignore_ascii_case(&name)) {
|
||||
net.jupes.push(Jupe { name, sid, reason });
|
||||
net.jupe_seq += 1;
|
||||
}
|
||||
}
|
||||
Event::JupeRemoved { name } => {
|
||||
net.jupes.retain(|j| !j.name.eq_ignore_ascii_case(&name));
|
||||
}
|
||||
Event::AccountExpiryWarned { account } => {
|
||||
if let Some(a) = accounts.get_mut(&key(&account)) {
|
||||
a.expiry_warned = true;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue