Log store write failures at the source; share the oper guard
The store's map_err(|_| Internal) discarded the real IO error, so a disk-full or permissions failure was invisible; log it once in the log append path where it happens. Add echo_api::require_oper (parameterised by the required privilege) and route the three copied per-service guards through it, keeping each service's own wording and policy.
This commit is contained in:
parent
081d90ad68
commit
d9c0878b00
5 changed files with 23 additions and 16 deletions
|
|
@ -1175,6 +1175,20 @@ pub fn help(me: &str, from: &Sender, ctx: &mut ServiceCtx, blurb: &str, topics:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Gate a command on operator privilege. `need` is the privilege required, or
|
||||||
|
// None for "any operator". Notices the user and returns false when denied,
|
||||||
|
// otherwise returns true. `what` names the action, e.g. "reviewing reports".
|
||||||
|
pub fn require_oper(me: &str, from: &Sender, ctx: &mut ServiceCtx, need: Option<Priv>, what: &str) -> bool {
|
||||||
|
let ok = match need {
|
||||||
|
Some(p) => from.privs.has(p),
|
||||||
|
None => from.privs.any(),
|
||||||
|
};
|
||||||
|
if !ok {
|
||||||
|
ctx.notice(me, from.uid, format!("Access denied — {what} is for services operators."));
|
||||||
|
}
|
||||||
|
ok
|
||||||
|
}
|
||||||
|
|
||||||
// Format a Unix timestamp (seconds) as "YYYY-MM-DD HH:MM:SS UTC", using Howard
|
// Format a Unix timestamp (seconds) as "YYYY-MM-DD HH:MM:SS UTC", using Howard
|
||||||
// Hinnant's civil-from-days algorithm so no date crate is needed.
|
// Hinnant's civil-from-days algorithm so no date crate is needed.
|
||||||
pub fn human_time(ts: u64) -> String {
|
pub fn human_time(ts: u64) -> String {
|
||||||
|
|
|
||||||
|
|
@ -87,11 +87,7 @@ impl Service for HelpServ {
|
||||||
|
|
||||||
// Working the help queue is operator-only.
|
// Working the help queue is operator-only.
|
||||||
fn require_oper(me: &str, from: &Sender, ctx: &mut ServiceCtx) -> bool {
|
fn require_oper(me: &str, from: &Sender, ctx: &mut ServiceCtx) -> bool {
|
||||||
if from.privs.any() {
|
echo_api::require_oper(me, from, ctx, None, "working the help queue")
|
||||||
return true;
|
|
||||||
}
|
|
||||||
ctx.notice(me, from.uid, "Access denied — working the help queue is for services operators.");
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Claim ticket `id` for the calling operator (shared by TAKE and NEXT).
|
// Claim ticket `id` for the calling operator (shared by TAKE and NEXT).
|
||||||
|
|
|
||||||
|
|
@ -103,11 +103,7 @@ impl Service for HostServ {
|
||||||
|
|
||||||
// Operator gate for vhost administration.
|
// Operator gate for vhost administration.
|
||||||
fn require_oper(me: &str, from: &Sender, ctx: &mut ServiceCtx) -> bool {
|
fn require_oper(me: &str, from: &Sender, ctx: &mut ServiceCtx) -> bool {
|
||||||
if from.privs.has(Priv::Admin) {
|
echo_api::require_oper(me, from, ctx, Some(Priv::Admin), "assigning vhosts")
|
||||||
return true;
|
|
||||||
}
|
|
||||||
ctx.notice(me, from.uid, "Access denied — assigning vhosts is for services operators.");
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Canonicalise a vhost the way the ircd will actually display it: the host part
|
// Canonicalise a vhost the way the ircd will actually display it: the host part
|
||||||
|
|
|
||||||
|
|
@ -66,9 +66,5 @@ impl Service for ReportServ {
|
||||||
|
|
||||||
// Reviewing the queue is for operators only.
|
// Reviewing the queue is for operators only.
|
||||||
fn require_oper(me: &str, from: &Sender, ctx: &mut ServiceCtx) -> bool {
|
fn require_oper(me: &str, from: &Sender, ctx: &mut ServiceCtx) -> bool {
|
||||||
if from.privs.any() {
|
echo_api::require_oper(me, from, ctx, None, "reviewing reports")
|
||||||
return true;
|
|
||||||
}
|
|
||||||
ctx.notice(me, from.uid, "Access denied — reviewing reports is for services operators.");
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -690,7 +690,12 @@ impl EventLog {
|
||||||
} else {
|
} else {
|
||||||
LogEntry { origin: self.origin.clone(), seq: 0, lamport: 0, event }
|
LogEntry { origin: self.origin.clone(), seq: 0, lamport: 0, event }
|
||||||
};
|
};
|
||||||
self.persist(&entry)?;
|
if let Err(e) = self.persist(&entry) {
|
||||||
|
// Surface the real cause (disk full, permissions, ...) here at the
|
||||||
|
// source; callers only see a generic Internal error further up.
|
||||||
|
tracing::error!(%e, path = %self.path.display(), "failed to write event to the log");
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
if global {
|
if global {
|
||||||
self.versions.insert(entry.origin.clone(), entry.seq);
|
self.versions.insert(entry.origin.clone(), entry.seq);
|
||||||
self.notify(&entry);
|
self.notify(&entry);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue